Cabbage Java self study room covers core knowledge

Eureka Spring Cloud Component Principle Series (1) Hystrix Spring Cloud component Principle series (2) Feign

1. Introduction of Feign

Feign is a lightweight framework for invoking Http requests as Java interface annotations. Spring Cloud introduces Feign and integrates the Ribbon for client-side load balancing calls.

What problem did Feign solve?

Encapsulates the Http call flow, which is more suitable for interface oriented change habits.

2. How Feign works

Feign Remote call flow:

  1. The implementation class is generated based on dynamic proxy
  2. The annotation information of the interface class is parsed into internal representations according to the Contract rules
  3. Dynamically generate Request based on RequestBean
  4. Use Encoder to convert the Bean into Http message body
  5. Interceptors are responsible for decorating requests and returns
  6. The logger logs requests
  7. Sends HTTP requests based on retries

2.1. Generate implementation classes based on dynamic proxy

When Feign is used, the corresponding interface class is defined and Http annotations are used on the interface class to identify Http request parameter information. At the Feign level, the implementation class is generated based on the dynamic proxy approach oriented to the interface, and the request invocation is delegated to the dynamic proxy implementation class. The basic principle is as follows:

2.2. Parse the annotation information of the interface class into internal representation according to the rules of the Contract

2.3. Dynamically generate Request based on RequestBean

The Http Request object is constructed by extracting values from the incoming Bean object and annotation information.

2.4. Use Encoder to convert the Bean into Http message body

Feign eventually converts the request into an Http message and sends it out, and the incoming request object is eventually parsed into the message body

2.5. Interceptors are responsible for decorating requests and returns

During the request transformation process, Feign abstracts the interceptor interface so that users can customize their actions on the request, such as defining a request interceptor if they want to be compressed during Http messaging.

2.6. The logger logs requests

2.7. Sending HTTP requests based on retries

Feign has a built-in retrayer that allows it to send a request with a maximum number of attempts when an IO exception occurs in an HTTP request.

Feign delegates the actual SENDING of HTTP requests to feign.client.

Feign a default by the JDK java.net.HttpURLConnection bottom implements Feign. Client interface classes, in each time you send a request, will create new HttpURLConnection link, This is why Feign’s performance is poor by default. You can extend this interface to use a high-performance connection pool-based Http client such as Apache HttpClient or OkHttp3.

Feign’s overall framework is very small and takes little time to process request transformations and message parsing. It is the processing of Http C requests that really affects performance.

3. Optimized Feign configuration

3.1. GZIP compression

Gzip is a data format that uses the Deflate algorithm to compress data. When gzip is compressed to a plain text data, the data size can be reduced by more than 70%.

What GZIP does: Compressed network data actually reduces the number of bytes transmitted over the network, with the most obvious benefit being faster web page loading.

Configure gZIP compression for Feign request-reply only:

# feign gzip
# local configuration. Configure GZIP compression only in HTTP request-reply related to Feign technology.
Gzip is used to compress data between application Client and Application Service.
Has nothing to do with communication between browser and Application Client.
# Enable feign request compression, Application Client -> Application Service
feign.compression.request.enabled=true
# Enable compression of feign technology responses, Application Service -> Application Client
feign.compression.response.enabled=true
Set the type of request/response that can be compressed.
feign.compression.request.mime-types=text/xml,application/xml,application/json
Use compression when the size of the requested data is reached. The default is 2048 bytes.
feign.compression.request.min-request-size=512
Copy the code

Configure global gzip compression:

# spring boot gzip
# Enable gzip compression in Spring Boot. Is a GZIP compression of all HTTP request-replies associated with the current application.
server.compression.enabled=true
The default is no limit on which client requests are not compressed
server.compression.excluded-user-agents=gozilla,traviata
# configuration to compress the request/response data type, is the default text/HTML, text/XML, text/plain
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
The default compression threshold is 2048
server.compression.min-response-size=512
Copy the code

3.2. Use HTTP connection pooling to provide performance

Feign’s HTTP client supports three frameworks, namely; HttpURLConnection, HttpClient, OKHttp. Feign uses HttpURLConnection by default.

  • HttpURLConnection is the JDK’s own HTTP client technology, which does not support connection pooling. To implement connection pooling, you need to manage connection objects yourself. For relatively complex operations such as network requests, there is no need to manage the connection objects yourself if alternative schemes are available.
  • Apache provides the HttpClient framework compared to the traditional JDK native HttpURLConnection, it encapsulates the access to HTTP request headers, parameters, content body, response and so on; It not only makes it easy for clients to send HTTP requests, but also facilitates developers to test interfaces (based on HTTP protocol), which improves the efficiency of development and the robustness of the code. In addition, when high concurrency large number of requests network, still use “HTTP connection pool” to improve throughput.
  • OKHttp is an open source project for handling network requests and is the most popular lightweight framework on Android. OKHttp has shared sockets, reducing the number of requests to the server, and reducing request latency through connection pooling.

The communication performance of Feign is improved by replacing Feign’s underlying HTTP client with HttpClient.

feign.httpclient.enabled=true
Copy the code

4. Feign and Ribbon

Feign is a simple encapsulation of the Ribbon.

The Ribbon uses @loadBalanced to load services. The Ribbon accesses the provider interface via Http (usually with RestTemplate) requests. The Ribbon uses interceptors/proxies to replace service names with IP addresses. If the providers are clustered, the Ribbon uses the appropriate algorithms (default polling, randomness, weights, and so on) for load balancing.

4.1. Ribbon source code analysis

  1. For LoadBalancerAutoConfiguration entrance

This configuration class simply initializes a few beans. Then use RestTemplateCustomizers to customize all of our Resttemplates with @loadBalanced annotations. An easy way to do this is to put interceptors that use @loadBalanced’s RestTemplate into the interceptor collection and make their interceptors work when using the RestTemplate.

@Configuration( proxyBeanMethods = false )
@ConditionalOnClass({RestTemplate.class})
@ConditionalOnBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
 
    // Holds all restTemplates modified by @loadBalanced
    @LoadBalanced
    @Autowired(required = false )
    private List<RestTemplate> restTemplates = Collections.emptyList();
 
    @Autowired(required = false)
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
 
    public LoadBalancerAutoConfiguration(a) {}/ / 1, SmartInitializingSingleton
    // This bean will use restTemplateCustomizers to customize all restTemplates
    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> {
            restTemplateCustomizers.ifAvailable((customizers) -> {
                Iterator var2 = this.restTemplates.iterator();
 
                while(var2.hasNext()) {
                    RestTemplate restTemplate = (RestTemplate)var2.next();
                    Iterator var4 = customizers.iterator();
 
                    while(var4.hasNext()) { RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next(); customizer.customize(restTemplate); }}}); }; }/ / 2, RestTemplateCustomizer
        // This bean is used to customize the restTemplate. The custom logic is to encapsulate the LoadBalancerInterceptor interceptor interceptor into the restTemplate interceptor collection (which is then handled uniformly by the restTemplate interceptor collection)
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
            return (restTemplate) -> {
                List<ClientHttpRequestInterceptor> list = newArrayList(restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; }}// LoadBalancerInterceptor, this is the interceptor we use to replace the IP request path
    @Configuration( proxyBeanMethods = false )
    @ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }
 
        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }
 
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return (restTemplate) -> {
                List<ClientHttpRequestInterceptor> list = newArrayList(restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; }}}Copy the code
  1. The interceptor implements the Intercept () method

When we use RestTemplate:

restTemplate.postForObject("http://cloud-payment-service"+"/payment/create", payment, CommonResult.class)
Copy the code

Where the URI intercepts the above request path and the servername gets the service name. The service name is then put into the execute() method, which will retrieve the real IP and port information of the corresponding service name based on the registry.

public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName ! =null."Request URI does not contain a valid hostname: " + originalUri);
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
Copy the code

Let’s go to the excute() method and see how we can get information like the real service IP.

 public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
        // get a load balancer
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        // 2. If there are multiple instances, select one instance based on the load balancer algorithm
        Server server = this.getServer(loadBalancer, hint);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request); }}Copy the code

We need to go in and see how he will get to a load balancer (here we don’t go in this method, because the last call is an interface, so we directly to look for his implementation class), find RibbonClientConfiguration configuration class.

@Configuration
@EnableConfigurationProperties
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
   
    @Bean
    @ConditionalOnMissingBean
    public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
        // If the configuration file has a configuration for which load balancer to use, the configuration file is preferred.
        // Otherwise use ZoneAwareLoadBalancer
        return (ILoadBalancer)(this.propertiesFactory.isSet(ILoadBalancer.class, this.name) ? (ILoadBalancer)this.propertiesFactory.get(ILoadBalancer.class, config, this.name) : new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, serverListUpdater));
    }
 
    // Load the load balancing rule rule
    @Bean
    @ConditionalOnMissingBean
    public IRule ribbonRule(IClientConfig config) {
        // Take it from the configuration file first
        if (this.propertiesFactory.isSet(IRule.class, this.name)) {
            return (IRule)this.propertiesFactory.get(IRule.class, config, this.name);
        } else {
            // If no, use the default load balancing rule
            ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
            rule.initWithNiwsConfig(config);
            returnrule; }}@Bean
    @ConditionalOnMissingBean
    public IPing ribbonPing(IClientConfig config) {
        return (IPing)(this.propertiesFactory.isSet(IPing.class, this.name) ? (IPing)this.propertiesFactory.get(IPing.class, config, this.name) : newDummyPing()); }}Copy the code

At this point we have found the default ZoneAwareLoadBalancer to see how this load balancer performs load balancing.

  1. The chooseServer() method in ZoneAwareLoadBalancer implements load balancing

If there are multiple instances, select one instance based on the load balancer algorithm. Enter getServer () method, this call the loadBalancer. The load balancer chooseServer () method, namely select an instance. The chooseServer() method gets an instance in conjunction with the registry’s information.

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
        return loadBalancer == null ? null: loadBalancer.chooseServer(hint ! =null ? hint : "default");
    }
Copy the code

To figure out how to match registration and load balancing, go back to the load balancer to see how the chooseServer() method is implemented.

public Server chooseServer(Object key) {
        if (ENABLED.get() && this.getLoadBalancerStats().getAvailableZones().size() > 1) {
            / / to omit}}else {
            // Enter the method
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key); }}Copy the code

Enter the super.chooseserver (key) method to see the implementation:

public Server chooseServer(Object key) {
        if (this.counter == null) {
            this.counter = this.createCounter();
        }
 
        this.counter.increment();
        if (this.rule == null) {
            return null;
        } else {
            try {
                return this.rule.choose(key);
            } catch (Exception var3) {
                logger.warn("LoadBalancer [{}]: Error choosing server for key {}".new Object[]{this.name, key, var3});
                return null; }}}Copy the code

Enter the rule-.choose () method. Discovery is an interface, open implementation class discovery is the so-called 8 load balancing algorithm implementation class.

public interface IRule {

    Server choose(Object var1);
 
    void setLoadBalancer(ILoadBalancer var1);
 
    ILoadBalancer getLoadBalancer(a);
    
}
Copy the code

And we the default is PredicateBasedRule the implementation class (polling), (actually injected from RibbonClientConfiguration ribbonRule ZoneAvoidanceRule is used in the default, PredicateBasedRule (PredicateBasedRule);

 
public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
    public PredicateBasedRule(a) {}public abstract AbstractServerPredicate getPredicate(a);
 
    public Server choose(Object key) {
        ILoadBalancer lb = this.getLoadBalancer();
        // The polling algorithm is used
        Optional<Server> server = this.getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
        return server.isPresent() ? (Server)server.get() : null; }}Copy the code

To enter the polling method:

public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) {
 
        // Get the collection of available instances. How to work with Nacos here and how to work with nacOS there
        List<Server> eligible = this.getEligibleServers(servers, loadBalancerKey);
        // incrementAndGetModulo implements polling algorithm
        return eligible.size() == 0 ? Optional.absent() : Optional.of(eligible.get(this.incrementAndGetModulo(eligible.size())));
    }
Copy the code

Go to the incrementAndGetModulo() method and look:

 private int incrementAndGetModulo(int modulo) {
        int current;
        int next;
        do {
            current = this.nextIndex.get();
            next = (current + 1) % modulo;
        } while(!this.nextIndex.compareAndSet(current, next) || current >= modulo);
 
        return current;
    }
Copy the code

Calculation method:

current = this.nextIndex.get();      
next = (current + 1) % modulo;  
Copy the code

Here we get the last next value, and then mod our current+1 value and store the CAS. The next time I take the next value.

For example, if we have two machines (representing 0 and 1 respectively), the modulo passed in is 2.

For the first time: current is 0, next is 1. At this time return 0 back, it represents access to the first machine (machine 0).

The second time: Corrent is 1 (last time next), next is 0, at this time return 1, it means to access the first machine.

4.2 Feign source code analysis

Feign’s source code is very simple. It uses proxies to wrap the Ribbon.

@Component
@FeignClient(value = "cloud-payment-service",fallback = PaymentHystrixServiceImpl.class)
public interface PaymentFeignService {
 
    @GetMapping(value = "/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id);
 
    @GetMapping(value="/payment/lb")
    public String getPaymentLB(a) throws InterruptedException; } Start class plus one@EnableFeignClientsannotationsCopy the code

Feign simply means that the Ribbon takes the instance information and adds the service name and interface address. Then it replaces the URI and splices it with the HTTP request.

Special consideration:

Look at this, and you’re like, isn’t the Ribbon doing this for us? Yes, the Ribbon does this, but to use the Ribbon, we need to inject a restTemplate with @loadBalanced, Then use the restTemplate to send the request itself (the Ribbon then modifs the URI for us internally). Feign doesn’t need us to write so much when it is used. Feign uses declarative interfaces to help us encapsulate the methods that inject restTemplate and call restTemplate manually. We just need to tell it which interface of which service we want to call.

In a nutshell, Feign encapsulates the Ribbon to make it easier to use.

Feign uses the okHttpclient.

Enter with the @enableFeignClients annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
    String[] value() default {};
 
    String[] basePackages() default{}; Class<? >[] basePackageClasses()default{}; Class<? >[] defaultConfiguration()default{}; Class<? >[] clients()default {};
}
Copy the code

Enter the @ Import ({FeignClientsRegistrar. Class}) this class, see registerBeanDefinitions () method, This method injects the @enableFeignClients configuration and @FeignClients interface into the Spring container:

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
 
        // Scan the configured information in @enableFeignClients and register
        this.registerDefaultConfiguration(metadata, registry);
 
        // Register FeignClients with the spring container
        this.registerFeignClients(metadata, registry);
    }
Copy the code

RegisterFeignClients (metadata, registry) registerFeignClients

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        / / scanner
        ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
        scanner.setResourceLoader(this.resourceLoader);
 
        // Scan all feignClient annotations and place them in the container
        Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        AnnotationTypeFilter annotationTypeFilter = newAnnotationTypeFilter(FeignClient.class); Class<? >[] clients = attrs ==null ? null : (Class[])((Class[])attrs.get("clients"));
        Object basePackages;
        if(clients ! =null&& clients.length ! =0) {
            final Set<String> clientClasses = new HashSet();
            basePackages = new HashSet();
            Class[] var9 = clients;
            int var10 = clients.length;
 
            for(int var11 = 0; var11 < var10; ++var11) { Class<? > clazz = var9[var11]; ((Set)basePackages).add(ClassUtils.getPackageName(clazz)); clientClasses.add(clazz.getCanonicalName()); } AbstractClassTestingTypeFilter filter =new AbstractClassTestingTypeFilter() {
                protected boolean match(ClassMetadata metadata) {
                    String cleaned = metadata.getClassName().replaceAll(\ \ "$".".");
                    returnclientClasses.contains(cleaned); }}; scanner.addIncludeFilter(new FeignClientsRegistrar.AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
        } else {
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = this.getBasePackages(metadata);
        }
 
        Iterator var17 = ((Set)basePackages).iterator();
 
        while(var17.hasNext()) {
            String basePackage = (String)var17.next();
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            Iterator var21 = candidateComponents.iterator();
 
            while(var21.hasNext()) {
                BeanDefinition candidateComponent = (BeanDefinition)var21.next();
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
                    Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
                    String name = this.getClientName(attributes);
                    this.registerClientConfiguration(registry, name, attributes.get("configuration"));
 
                    // Enter the registerFeignClient method
                    this.registerFeignClient(registry, annotationMetadata, attributes); }}}}Copy the code

Enter the registerFeignClient(Registry, annotationMetadata, Attributes) method:

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
 
        // Generate the proxy factory bean (FeignClientFactoryBean),
        // Add the feignClient annotation information to the factory bean and put it into the Spring container
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
        this.validate(attributes);
 
        definition.addPropertyValue("url".this.getUrl(attributes));
        definition.addPropertyValue("path".this.getPath(attributes));
        String name = this.getName(attributes);
        definition.addPropertyValue("name", name);
        String contextId = this.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(2);
        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        boolean primary = (Boolean)attributes.get("primary");
        beanDefinition.setPrimary(primary);
        String qualifier = this.getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }
 
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }
Copy the code

Enter the factory FeignClientFactoryBean, where a loadBalance() method generates proxy objects using JDK dynamic proxies.

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean.ApplicationContextAware {
 
 protected <T> T loadBalance(Builder builder, FeignContext context, HardCodedTarget<T> target) {
        Client client = (Client)this.getOptional(context, Client.class);
        if(client ! =null) {
            builder.client(client);
            Targeter targeter = (Targeter)this.get(context, Targeter.class);
            return targeter.target(this, builder, context, target);
        } else {
            throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?"); }}}Copy the code

Finally the JDK dynamic proxy generates a proxy object for LoadBalancerFeignClient. There is a execute() method in this class, and when we call it using the Feign interface, we are actually calling its execute() method using the proxy object of the LoadBalancerFeignClient.

public class LoadBalancerFeignClient implements Client {
 
    public Response execute(Request request, Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
 
            // Use ribbonRequest to call the integrated Ribbon for load balancing
            RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, uriWithoutHost);
            IClientConfig requestConfig = this.getClientConfig(options, clientName);
            return ((RibbonResponse)this.lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse();
        } catch (ClientException var8) {
            IOException io = this.findIOException(var8);
            if(io ! =null) {
                throw io;
            } else {
                throw newRuntimeException(var8); }}}}Copy the code

How Feign and Ribbon combine:

  1. Feign uses dynamic proxies to get a proxy factory bean: FeignClientFactoryBean, which eventually generates a proxy object of LoadBalancerFeignClient;
  2. When we call the Feign interface, the Feign proxy object will call the execute() method to concatenate urls (urls with service names). Then, the RibbonRequest is used to call the integrated Ribbon for load balancing.
  3. Finally, Feign gets the concatenated URL (the one that replaced the service name) and makes the HTTP request. (When the Ribbon receives the call, The Ribbon uses an interceptor to call the Execute () method of the Ribbon itself to implement load balancing. Feign uses the Ribbon to replace service names, service lists, and so on.

Eureka Spring Cloud Component Principle Series (1) Hystrix Spring Cloud component Principle series (2) Feign