一、ribbon如何集成在openfeign中使用


所有文章

https://www.cnblogs.com/lay2017/p/11908715.html

 

正文

ribbon是springcloud封裝的一個基於http客戶端負載均衡的組件。springcloud的openfeign集成使用了ribbon。所以如果你使用openfeign,那么也會很輕易得使用到ribbon。本文將從openfeign切入,看看它是怎么來使用到ribbon這個客戶端負載均衡組件的。

LoadBalancerFeignClient提供openfeign的負載均衡實現

在講openfeign的時候我們說到,最后代理類其實就是發起http請求,並解碼返回的字節碼內容。回顧一下代碼

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);

    Response response;

    try {
      response = client.execute(request, options);
    } catch (IOException e) {
    }


    boolean shouldClose = true;
    try {
      if (Response.class == metadata.returnType()) {
        // 
      }
      if (response.status() >= 200 && response.status() < 300) {
        // 返回空
        if (void.class == metadata.returnType()) {
          return null;
        } else {
          // 返回解碼結果對象
          Object result = decode(response); return result;
        }
      } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
        //
      } else {
        //
      }
    } catch (IOException e) {
      // 
    } finally {
      // 
    }
  }

可以看到,client提交了一個http的request,然后獲得了response響應對象,處理后並返回。ribbon的接入將從這里開始,我們看看client接口

public interface Client {
  
  Response execute(Request request, Options options) throws IOException;  
}

接口很簡單,就是一個請求響應模型。那么,我們再向下看看Client關於負載均衡這個方面有什么實現呢?

LoadBalancerFeignClient作為Client在負載均衡方面的實現類,我們跟進它的execute方法,看看做了些啥

public Response execute(Request request, Request.Options options) throws IOException {
    try {
      URI asUri = URI.create(request.url());
      String clientName = asUri.getHost();
      URI uriWithoutHost = cleanUrl(request.url(), clientName);
      // 構造ribbon的request對象
      FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(this.delegate, request, uriWithoutHost);

      IClientConfig requestConfig = getClientConfig(options, clientName);
      // 執行ribbon的request對象
      return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
    } catch (ClientException e) {
      //
    }
  }

可以看到,關於負載均衡方面openfeign直接構造了ribbon的請求,並執行。

 

構造ribbon的IClient

lbClient(clientName)構造了一個ribbon的http客戶端實現,打開該方法

private FeignLoadBalancer lbClient(String clientName) {
  return this.lbClientFactory.create(clientName);
}

一個簡單工廠模式,跟進create方法

public FeignLoadBalancer create(String clientName) {
    FeignLoadBalancer client = this.cache.get(clientName);
    if (client != null) {
      return client;
    }
    IClientConfig config = this.factory.getClientConfig(clientName);
    ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
    ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
    // 默認返回FeignLoadBalancer
    client = this.loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector, this.loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
    this.cache.put(clientName, client);
    return client;
  }

這里瞄一眼FeignLoadBalancer的類圖吧

FeignLoadBalancer實現了IClient接口,所以它會負責提交並執行Ribbon的request請求

 

提交執行ribbon的request請求

lbClient方法構建了FeignLoadBalancer,下面該調用它的executeWithLoadBalancer方法了,跟進方法(方法在父類AbstractLoadBalancerAwareClient中)

public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);

    try {
        return command.submit(
            new ServerOperation<T>() {
                @Override
                public Observable<T> call(Server server) {
                    // 回調返回選擇好的Server對象,並重新構造uri地址
                    URI finalUri = reconstructURIWithServer(server, request.getUri());
                    S requestForServer = (S) request.replaceUri(finalUri);
                    try {
                        // 執行ribbon的request請求
                        return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                    } 
                    catch (Exception e) {
                        return Observable.error(e);
                    }
                }
            })
            .toBlocking()
            .single();
    } catch (Exception e) {
        // 
    }
    
}

如果不關心負載均衡的情況,這里其實就是直接執行ribbon的request請求了。也就是IClient這個接口類定義的內容。不過FeignLoadBalancer需要進行一次負載選擇Server,然后才回調這里的call來發起請求。

 

跟進submit方法看看,submit方法先是進行了一次選擇獲得了一個Server對象,然后回調了上面說的ribbon的request執行

public Observable<T> submit(final ServerOperation<T> operation) {
        // ...

        // Use the load balancer
        Observable<T> o = 
                // 選擇Server
                (server == null ? selectServer() : Observable.just(server))
                .concatMap(new Func1<Server, Observable<T>>() {
                    @Override
                    // Called for each server being selected
                    public Observable<T> call(Server server) {
                        context.setServer(server);
                        //
                        
                        // Called for each attempt and retry
                        Observable<T> o = Observable
                                .just(server)
                                .concatMap(new Func1<Server, Observable<T>>() {
                                    @Override
                                    public Observable<T> call(final Server server) {
                                        // ...
                                        // 回調ribbon的request請求
                                        return operation.call(server).doOnEach(
                                            // ...
                                        );
                                    }
                                });
                        
                        if (maxRetrysSame > 0) 
                            o = o.retry(retryPolicy(maxRetrysSame, true));
                        return o;
                    }
                });
            
        // ...
    }

 

ILoadBalancer負載均衡器

繼續跟進selectServer,看看如何選擇服務的

private Observable<Server> selectServer() {
    return Observable.create(new OnSubscribe<Server>() {
        @Override
        public void call(Subscriber<? super Server> next) {
            try {
                // 從上下文中獲取
                Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   
                next.onNext(server);
                next.onCompleted();
            } catch (Exception e) {
                next.onError(e);
            }
        }
    });
}

托付給了getServerFromLoadBalancer來實現,繼續跟進

public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
    // ...

    ILoadBalancer lb = getLoadBalancer();
    if (host == null) {
        if (lb != null){
            Server svc = lb.chooseServer(loadBalancerKey);
            // ...

            return svc;
        } else {
            // ...
        }
    } else {
        // ...
    }
    
    // ...
}

getLoadBalancer方法先是獲取了一個ILoadBalancer接口的實現,然后調用了chooseServer來選擇一個Server。

先跟進getLoadBalancer方法,直接返回了上下文中的設置的ILoadBalancer負載均衡器

private ILoadBalancer lb;

public ILoadBalancer getLoadBalancer() {
    return lb;    
}

我們看一下ILoadBalancer的的類圖,RibbonClientConfiguration配置ILoadBalancer的時候配置的是ZoneAwareLoadBalancer的Bean

getLoadBalancer返回的ILoadBalancer負載均衡器也將是ZoneAwareLoadBalancer的實例對象

 

IRule負載均衡算法

有了ILoadBalancer負載均衡器,再看看chooseServer方法。這里忽略一些細節,直接看BaseLoadBalancer的chooseServer這個核心的實現

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

可以看看,直接調用了IRule接口的choose方法。IRule接口則負責相關的負載均衡算法實現,我們看看IRule接口有哪些實現吧

常見的隨機算法、輪詢算法...等

 

總結

到這里,本文就結束了。我們再回顧一下文章的接口和流程

1、先是openfeign開放了一個Client接口用於http請求,並且LoadBalancerFeignClient作為負載均衡的實現類

2、LoadBalancerFeignClient則直接構造了一個ribbon的IClient接口的實現FeignLoadBalancer

3、執行ribbon的request之前,先委托ILoadBalancer負載均衡器選擇一個Server,然后回調執行request請求

4、ILoadBalancer會選擇IRule實現的負載均衡算法來獲取一個Server,並返回。

總體邏輯比較簡單,本文忽略了一些細節內容,比如一些自動配置的東西、如果從Eureka中獲取服務列表等,有興趣可以自己看看。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM