This content:

  • Relationship between OpenFeign and Feign
  • Feign underlying implementation principle
  • What is the Ribbon
  • Ribbon Underlying implementation principle
  • How does the Ribbon implement failed retry?

Relationship between OpenFeign and Feign

Feign is a lightweight restful HTTP service client in the Spring Cloud component that simplifies interface calls, turns HTTP calls into RPC calls, and makes calling remote interfaces as simple as calling interfaces within the same process application.

As with DUbbo’s RPC remote invocation, the invocation of the interface is implemented through a dynamic proxy. Feign simplifies the invocation of the interface by wrapping the request body, sending the HTTP request, getting the interface response, and serializing the response.

Openfeign supports Spring MVC annotations, such as @RequesMapping, @getMapping, @PostMapping, and so on. Openfeign also implements integration with the Ribbon.

The service provider only needs to provide the API and does not need to enforce the implements interface as dubbo does. Using Fegin does not require the service provider to implement the interface using the Implements keyword in Controller.

Feign underlying implementation principle

Openfeign scans all the interfaces annotated by the @FeignClient annotation through the package scan and registers an instance of FeignClientFactoryBean

for each interface. FeignClientFactoryBean

is a FactoryBean

. When Spring calls the getObject method of FeignClientFactoryBean

, OpenFeign returns a dynamic proxy class generated by Feign. Intercept method execution.

Feign generates a MethodHandler for each Method of the agent’s interface.

When configuring the service provider URL for the @FeignClient annotation url property on an interface, you are not integrated with the Ribbon. The SynchronousMethodHandler implements remote calls to interface methods. Use the Default instance of the Client implementation class Default to initiate the HTTP request.

If the @feignClient url attribute is not configured on an interface, load balancing logic is applied. This is required with the Ribbon. Instead of using the Default Client interface (Default), the LoadBalancerFeignClient interface (LoadBalancerFeignClient) is used to call the LoadBalancerFeignClient interface (LoadBalancerFeignClient). Integration with the Ribbon is implemented by LoadBalancerFeignClient.

What is the Ribbon

Ribbon is the open source project released Netflix, provides in the service consumer side load balancing to invoke the service provider, all of the available service providers, read from the registry on the client side is used when each call interface such as polling load balancing algorithm to select the call a service provider, therefore, Ribbon is a client load balancer.

The Ribbon provides multiple load balancing algorithms and retry support. Feign also supports retries, implemented in the SynchronousMethodHandler’s Invoke method. Feign’s retries are simpler, sending a request to the same service node, whereas the Ribbon’s failed retries are rescaling a service node. In the case of multiple nodes deployed by a service provider, Feign’s retry mechanism clearly doesn’t make much sense.

Ribbon Underlying implementation principle

Here’s an interview question I googled. Do you think it’s the right answer?

The bridge that the Ribbon integrates with Fegin is FeignLoadBalancer.

  • The Ribbon registers an ILoadBalancer (the default implementation class ZoneAwareLoadBalancer). Feign calls FeignLoadBalancer’s executeWithLoadBalancer method with the LoadBalancerFeignClient to use the ILoadBalancer load balancer in the Ribbon to select a provider to send HTTP requests The actual request is initiated by OpenFeign’s FeignLoadBalancer. The Ribbon selects only one service node from the start.

  • 2. The Spring-Cloud-Netflixribbon auto-configuration class registers a RibbonLoadBalancerClient, The RibbonLoadBalancerClient is the implementation class provided by the Ribbon for the Load balancing interface of spring Cloud, and is used to implement the @loadbalancer annotation.

The Ribbon does not obtain service providers from the registry directly through DiscoveryClient. Instead, the Ribbon obtains service providers from the registry through ServerList

, which is different from DiscoveryClient. ServerList is not an interface defined by Spring Cloud, but one defined by the Ribbon. Take the Spring-cloud-Kubernetes-ribbon as an example. The Spring-cloud-Kubernetes-ribbon provides a ServerList implementation for the ribbon, KubernetesServerList. The Ribbon periodically updates available service providers by calling the getUpdatedListOfServers method of the ServerList.

How to get the available service provider nodes is up to you to implement the ServerList interface and register the implemented ServerList with the Spring container. If you do not provide ServerList, then use the default implementation class will be a Ribbon with ConfigurationBasedServerList, ConfigurationBasedServerList will not read from the registry access service node, Instead, it is read from a configuration file.

If we use the Eureka registry, when we add the spring-cloud-starter- Netflix – Eureka-client to the project, we have already imported a ribbon- Eureka jar into the project. By the jars with Ribbon and had to integrate the ServerList: DiscoveryEnabledNIWSServerList.

Ribbon – eureka source address: https://github.com/Netflix/ribbon/tree/master/ribbon-eureka, interested friends may have a look.

Now, would you still say that the Ribbon gets service providers from the registry through DiscoveryClient? Of course, you can implement your own ServerList and get it from the registry via DiscoveryClient.

How does the Ribbon implement failed retry?

Ribbon provide RetryHandler interface and use DefaultLoadBalancerRetryHandler by default. In the submit method of LoadBalancerCommand (called in FeignLoadBalancer’s executeWithLoadBalancer method), if the number of retries configured is greater than zero, the RxJava API is called to support retries.

public Observable<T> submit(final ServerOperation<T> operation) {
        / /...
        final int maxRetrysSame = retryHandler.getMaxRetriesOnSameServer();
        final int maxRetrysNext = retryHandler.getMaxRetriesOnNextServer();
        // Use the load balancer
        Observable<T> o = (server == null ? selectServer() : Observable.just(server))
                .concatMap(new Func1<Server, Observable<T>>() {
                    @Override
                    public Observable<T> call(Server server) {
                        / /...
                        // The number of retries to invoke the same node
                        if (maxRetrysSame > 0) 
                            o = o.retry(retryPolicy(maxRetrysSame, true));
                        returno; }});// Call the number of retries for different nodes
        if (maxRetrysNext > 0 && server == null) 
            o = o.retry(retryPolicy(maxRetrysNext, false));
        returno.onErrorResumeNext(...) ; }Copy the code

The default value of maxRetrysSame is 0, and the default value of maxRetrysNext is 1. The retryPolicy method returns a decision maker who determines whether a retry is needed (whether the exception thrown is allowed to retry and whether the maximum number of retries is reached).

private Func2<Integer, Throwable, Boolean> retryPolicy(final int maxRetrys, final boolean same) {
        return new Func2<Integer, Throwable, Boolean>() {
            @Override
            public Boolean call(Integer tryCount, Throwable e) {
                if (e instanceof AbortExecutionException) {
                    return false;
                }
                // Greater than the maximum retry times
                if (tryCount > maxRetrys) {
                    return false;
                }
                if(e.getCause() ! =null && e instanceof RuntimeException) {
                    e = e.getCause();
                }
                // Call RetryHandler to determine whether to retry
                returnretryHandler.isRetriableException(e, same); }}; }Copy the code