Feign use
Feign = RestTemplate+Ribbon+Hystrix
Feign implements RestTemplate+Ribbon effect
Feign implements the RestTemplate+Ribbon effect by adding openFeign configuration to the POM file of the springCloud project callers in the following steps
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Copy the code
Add @enableFeignClients to the startup class and implement interface calls using interface declarations
@FeignClient(name = "zhao-service-resume")
public interface ResumeFeignClient {
@GetMapping("/resume/openstate/{userId}")
public Integer findDefaultResumeState(@PathVariable Long userId) ;
}
Copy the code
@feignClient (name = “zhao-service-resume”) to declare the service to be called. The default way to call the service is to use unit tests to test the load balancing effect. Access it twice. Services accessing ports 8081 and 8082 respectively
@RunWith(SpringRunner.class) @SpringBootTest(classes = DeliverApplication8091.class) public class FeignTest { @Autowired ResumeFeignClient feignClient; @Test public void feignTest(){ Integer port = feignClient.findDefaultResumeState(2195321L); System.out.println(" test result "+port); }}Copy the code
So how do you configure load balancing in a configuration class? The format is as follows, and we have also configured the timeout period for the request. If Hystrix is not configured, the timeout will occur.
Zhao-service-resume: Ribbon: # Re-ribbon: # re-ribbon: # re-ribbon: # re-ribbon: # re-ribbon: # re-ribbon: # re-ribbon: 5000 # to retry OkToRetryOnAllOperations all operations are: True #### According to the configuration above, when accessing a fault request, it attempts to access the current instance again (the number of times is set by MaxAutoRetries). #### If it fails, it attempts to access the current instance again. If it fails, it attempts to access another instance. Change the instance access again (the number of changes is configured by MaxAutoRetriesNextServer), #### if it still fails, return a failure message. MaxAutoRetries: 0 # for the currently selected instance retries, not including the first call MaxAutoRetriesNextServer: 0 # switch case of retries NFLoadBalancerRuleClassName: Com.net flix. Loadbalancer. # RoundRobinRule load strategy adjustmentCopy the code
Timeout error feign. RetryableException: Read timed out executing the GET http://zhao-service-resume/resume/openstate/2195321 is not configured to retry this several parameters is the same effect
Feign implements the Hystrix effect
The first step is to turn on the fuse
feign:
hystrix:
enabled: true
Copy the code
Then add the configuration for the timeout handling logic
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 15000
Copy the code
But even though I was only asleep for ten seconds on the called side, the program was still fusing. A review of data indicates that Hystrix will use the smaller of feign and Hystrix timeouts for the timeout determinationAdded the downgrading method
@Component public class FeignClientFallBack implements ResumeFeignClient { @Override public Integer findDefaultResumeState(Long userId) { return -1; }}Copy the code
Add the degrade configuration to the caller
@FeignClient(name = "zhao-service-resume",fallback = FeignClientFallBack.class)
public interface ResumeFeignClient {
@GetMapping("/resume/openstate/{userId}")
public Integer findDefaultResumeState(@PathVariable Long userId) ;
}
Copy the code
You can also add the path attribute to @FeignClient to extract the common path on the method into the class
Other features on Feign usage
Feign Request compression and response compression configuration
Configure the following properties
feign: compression: request: enabled: true min-request-size: 2048 mime-types: Text/HTML, application/XML, application/json response # set compressed data type: enabled: trueCopy the code
The preceding configuration contains two attributes: min-request-size: 2048 indicates that the minimum value for enabling compression is 2048 bytes. Mime-types indicates data types that support compression. The current values of these types are not default
Feign Request log configuration
Start by setting the logging response level for specific classes in YML
Logging: level: # Feign log will only respond to the log level to debug com.lagou.edu.controller.service.ResumeServiceFeignClient: debugCopy the code
Then there is the log configuration for Feign
import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { @Bean Logger.Level feignLevel(){ return Logger.Level.FULL; }}Copy the code
It is important to note that the input here is feign.logger, which means that Feign will print all the requested information as follows
Feign source brief analysis
Again, look at the bootstrap class annotations and the auto-configuration classes configured in Spring.Factories. The FeignClientsRegistrar @enableFeignClients annotation is still the implementation of the Spring injection beanDefinition registrar
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
Copy the code
RegisterDefaultConfiguration into default configuration we can confirm the basic is to join some default configuration, and registerFeignClients is finally realize logic. The final place to implement the logic is under this method
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
Copy the code
And this class depends on the BeanDefinitionBuilder definition. = BeanDefinitionBuilder genericBeanDefinition (FeignClientFactoryBean. Class); It’s almost certain that the FeignClientFactoryBean is the factory Bean at the time of injection, so let’s look inside. The most important thing about the factory Bean is the type that getObject returns
@Override public Object getObject() throws Exception { return getTarget(); } /** * @param <T> the target type of the Feign client * @return a {@link Feign} client created with the specified data and the context information */ <T> T getTarget() { FeignContext context = applicationContext.getBean(FeignContext.class); Feign.Builder builder = feign(context); if (! StringUtils.hasText(this.url)) { if (! this.name.startsWith("http")) { url = "http://" + this.name; } else { url = this.name; } url += cleanPath(); return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, url)); } if (StringUtils.hasText(this.url) && ! this.url.startsWith("http")) { this.url = "http://" + this.url; } String url = this.url + cleanPath(); Client client = getOptional(context, Client.class); if (client ! = null) { if (client instanceof LoadBalancerFeignClient) { // not load balancing because we have a url, // but ribbon is on the classpath, so unwrap client = ((LoadBalancerFeignClient)client).getDelegate(); } builder.client(client); } Targeter targeter = get(context, Targeter.class); return (T) targeter.target(this, builder, context, new HardCodedTarget<>( this.type, this.name, url)); }Copy the code
In the above code, the client is basically constructed and invoked. The most important thing is the loadBalance operation that implements load balancing in the Ribbon
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) { Client client = getOptional(context, Client.class); if (client ! = null) { builder.client(client); Targeter targeter = get(context, Targeter.class); return targeter.target(this, builder, context, target); } throw new IllegalStateException( "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?" ); }Copy the code
The targeter.target will be executed in this method in the feign class
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
Copy the code
Notice that the Final implementation of the newInstance method is found in the ReflectiveFeign class
@Override public <T> T newInstance(Target<T> target) { Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>(); List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>(); for (Method method : target.type().getMethods()) { if (method.getDeclaringClass() == Object.class) { continue; } else if (Util.isDefault(method)) { DefaultMethodHandler handler = new DefaultMethodHandler(method); defaultMethodHandlers.add(handler); methodToHandler.put(method, handler); } else { methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } } InvocationHandler handler = factory.create(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<? >[] {target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy; }Copy the code
As can be seen above, the final generated class is actually a proxy class to complete the final call, while the proxy object completes the final load balancing processing, generating the invoke method of the dead FeignInvocationHandler used by the proxy object
static final class Default implements InvocationHandlerFactory { @Override public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) { return new ReflectiveFeign.FeignInvocationHandler(target, dispatch); }}Copy the code
Finally, the relevant codec operation is performed
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
Copy the code
And execute and decoding operation executeAndDecode is most important in the client. The execute method, point in later found that actually the final call is LoadBalancerFeignClient. The execute methodFinally, remote call and load balancing are implemented in this method
Welcome to search attention to my public number [micro view technology], and summary of classified interview questions github.com/zhendiao/Ja…