ITPub博客

首页 > 数据库 > 数据库开发技术 > OpenFeign是怎样集成Ribbon负载均衡能力的?

OpenFeign是怎样集成Ribbon负载均衡能力的?

原创 数据库开发技术 作者:服务端技术栈 时间:2023-06-19 08:10:00 0 删除 编辑

前言

上一篇文章讲解了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. */@ConditionalOnClass({ ILoadBalancer.class, Feign.class })@Configuration@AutoConfigureBefore(FeignAutoConfiguration.class)@EnableConfigurationProperties({ FeignHttpClientProperties.class })@Import({ HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class })public class FeignRibbonClientAutoConfiguration {
//自动配置一个缓存功能的负载均衡客户端工厂 @Bean @Primary @ConditionalOnMissingBean @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate") public CachingSpringLoadBalancerFactory cachingLBClientFactory( SpringClientFactory factory) { return new CachingSpringLoadBalancerFactory(factory); }
//自动配置一个缓存功能的负载均衡客户端工厂,并且具备重试能力 @Bean @Primary @ConditionalOnMissingBean @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate") 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执行流程

FeignLoadBalancer创建完后在哪用呢?其实就是在LoadBalancerFeignClient中会有使用,我们开始看LoadBalancerFeignClient的execute执行请求流程,在execute方法中,我们可以清晰的看到发起通信的主流程,里面就有一步获取FeignLoadBalancer。

看下源码,确实和我们平时发起http通信的流程非常相似

public Response execute(Request request, Request.Options options) throws IOException {            //构建url            URI asUri = URI.create(request.url());            String clientName = asUri.getHost();            //将url剔除服务名字             //http://127.0.0.1:8080/test/feign==>            //http:///test/feign            URI 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(@Nullable URI original, @Nullable 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("://");            //拼接ip            sb.append(host);            //拼接端口            sb.append(":").append(port);            //拼接路径            sb.append(original.getRawPath());            //构建URL            URI 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对象在负载均衡中的实现,不见不散哦。

原文作者:服务端技术栈;如有侵权,请联系管理员删除。

下一篇: 父亲节快乐
请登录后发表评论 登录
全部评论

注册时间:2019-03-08

  • 博文量
    97
  • 访问量
    41429