所有文章
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中獲取服務列表等,有興趣可以自己看看。