
前言
上一篇文章讲解了Feign框架最核心可扩展组件通信Client详解,feign.Client默认实现是feign.Client.Default,他使用了HttpURLConnection作为通信组件,如果是在微服务的使用场景下,默认的Client缺乏负载均衡的能力,使用场景有限,因此Spring Cloud团队进行了扩展,开发了具备通信能力和负载均衡能力的LoadBalancerFeignClient,由于负载均衡能力默认使用ribbon提供,因此本文单独分析负载均衡能力如何和OpenFeign集成的。
下面我们开始介绍今天的主角 LoadBalancerFeignClient
开车之前,先把整理的LoadBalancerFeignClient主流程时序图贴出来,逻辑还是非常清晰的,看完整篇文章再来看这个图,应该会更加理解深刻。

LoadBalancerFeignClient初探
我们先可以看到LoadBalancerFeignClient的构造函数
public class LoadBalancerFeignClient implements Client {static final Request.Options DEFAULT_OPTIONS = new Request.Options();//通信客户端private final Client delegate;//负载均衡客户端工厂private CachingSpringLoadBalancerFactory lbClientFactory;//Spring工厂private SpringClientFactory clientFactory;public LoadBalancerFeignClient(Client delegate,CachingSpringLoadBalancerFactory lbClientFactory,SpringClientFactory clientFactory) {this.delegate = delegate;this.lbClientFactory = lbClientFactory;this.clientFactory = clientFactory;}}
除了拥有feign.Client的引用外,还有1个和负载均衡相关的参数 CachingSpringLoadBalancerFactory,这个负载均衡客户端工厂在哪创建的呢?在openfeign包自动装配类FeignRibbonClientAutoConfiguration,我们可以找到答案
package org.springframework.cloud.openfeign.ribbon;/*** Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a* load balancer.*/OkHttpFeignLoadBalancedConfiguration.class,DefaultFeignLoadBalancedConfiguration.class })public class FeignRibbonClientAutoConfiguration {//自动配置一个缓存功能的负载均衡客户端工厂public CachingSpringLoadBalancerFactory cachingLBClientFactory(SpringClientFactory factory) {return new CachingSpringLoadBalancerFactory(factory);}//自动配置一个缓存功能的负载均衡客户端工厂,并且具备重试能力public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {return new CachingSpringLoadBalancerFactory(factory, retryFactory);}}
CachingSpringLoadBalancerFactory里面有一个最核心的方法,就是创建 FeignLoadBalancer
public FeignLoadBalancer create(String clientName) {//查缓存FeignLoadBalancer client = (FeignLoadBalancer)this.cache.get(clientName);if (client != null) {return client;} else {IClientConfig config = this.factory.getClientConfig(clientName);ILoadBalancer lb = this.factory.getLoadBalancer(clientName);ServerIntrospector 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 (FeignLoadBalancer)client;}}
这里有两个比较有意思的设计,Spring框架源码中比较常见:
第一个是CachingSpringLoadBalancerFactory创建完FeignLoadBalancer之后,就会存入map缓存,避免二次构建。
volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap();
key是服务提供者名称,value为FeignLoadBalancer或者RetryableFeignLoadBalancer

第二个点是openfeign为每一个服务提供者创建一个负载均衡客户端,相互隔离。
FeignLoadBalancer执行流程

看下源码,确实和我们平时发起http通信的流程非常相似
public Response execute(Request request, Request.Options options) throws IOException {//构建urlURI asUri = URI.create(request.url());String clientName = asUri.getHost();//将url剔除服务名字//http://127.0.0.1:8080/test/feign==>//http:///test/feignURI uriWithoutHost = cleanUrl(request.url(), clientName);//创建请求对象FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(this.delegate, request, uriWithoutHost);//请求配置IClientConfig requestConfig = this.getClientConfig(options, clientName);//构建FeignLoadBalancer并发起请求return ((FeignLoadBalancer.RibbonResponse)this.lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse();}
发起通信流程和我们平时写http请求的流程如出一辙,LoadBalancerFeignClient将请求发起的动作委托给了FeignLoadBalancer,也就是包了一层,和我们平台写的facade模式一样。
接下来我们需要分析FeignLoadBalancer的执行流程,FeignLoadBalancer继承了抽象类 AbstractLoadBalancerAwareClient,AbstractLoadBalancerAwareClient.executeWithLoadBalancer里面是一个模版方法设计模式的体现,它专门负责负载均衡的逻辑,而通信逻辑由子类实现,FeignLoadBalancer就是是其子类,负责发起请求通信。
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {//通过负载均衡能力,获取服务Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);//负载均衡选择的服务信息里拿到ip,port,URI finalUri = reconstructURIWithServer(server, request.getUri());S requestForServer = (S) request.replaceUri(finalUri);try {//发起通信return AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig);}catch (Exception e) {return Observable.error(e);}}
从getServerFromLoadBalancer这个方法名很容易看出来,他是通过负载均衡器获取服务。
public Server getServerFromLoadBalancer( URI original, Object loadBalancerKey) throws ClientException {Server svc = lb.chooseServer(loadBalancerKey);return svc;}
最终通过Rule对象选择一个服务
public Server chooseServer(Object key) {if (counter == null) {counter = createCounter();}counter.increment();return rule.choose(key);}
Rule模块的实现内容比较,咱们先不看,我们接着看AbstractLoadBalancerAwareClient.executeWithLoadBalancer 发起通信的主流程, reconstructURIWithServer方法则是根据负载均衡返回的Server信息重构请求url,逻辑非常清晰。
public URI reconstructURIWithServer(Server server, URI original) {//从服务信息获取ip,port,协议String host = server.getHost();int port = server.getPort();String scheme = server.getScheme();try {StringBuilder sb = new StringBuilder();//拼接协议sb.append(scheme).append("://");//拼接ipsb.append(host);//拼接端口sb.append(":").append(port);//拼接路径sb.append(original.getRawPath());//构建URLURI newURI = new URI(sb.toString());return newURI;} catch (URISyntaxException e) {throw new RuntimeException(e);}}
到这里请求url已经组装完整了,可以发起请求了,这个时候通过execute方法执行请求
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride) throws IOException {Request.Options options;//将ribbon的配置覆盖feign请求配置覆盖if (configOverride != null) {RibbonProperties override = RibbonProperties.from(configOverride);options = new Request.Options((long)override.connectTimeout(this.connectTimeout), TimeUnit.MILLISECONDS, (long)override.readTimeout(this.readTimeout), TimeUnit.MILLISECONDS, override.isFollowRedirects(this.followRedirects));} else {options = new Request.Options((long)this.connectTimeout, TimeUnit.MILLISECONDS, (long)this.readTimeout, TimeUnit.MILLISECONDS, this.followRedirects);}//调用通信客户端发起请求Response response = request.client().execute(request.toRequest(), options);return new RibbonResponse(request.getUri(), response);}
再次把LoadBalancerFeignClient发起通信的主体流程通过下方时序图表示出来,还是非常清晰的

总结篇
本文分析了LoadBalancerFeignClient整体的执行流程,里面涉及到Rule组件没有详细分析,Rule在ribbon中是比较重要的一个组件,他可以从配置文件或者注册中心读取配置,内容会占用比较多的篇幅,避免阅读体验不好,本文不详细剖析,下文将补充这块的内容,咱们下篇文章接着分析Rule对象在负载均衡中的实现,不见不散哦。
原文作者:服务端技术栈;如有侵权,请联系管理员删除。