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…