feign-eureka-ribbon的协作原理

feign-eureka-ribbon的协作原理

在我们的项目中使用了feigneurekaribbon这三个组件,最近想要在负载均衡上做些文章,需要了解这三个组件底层是如何协作的,这样才能找到突破口,所以给这三个组件的源码大概翻了一遍,最终整理出该笔记,希望对同样对这三个组件是如何协作感兴趣的读者一些帮助;

文中使用的spring cloud版本为Greenwich.SR6

PS: 本文为纯源码分析,所以配合源码阅读本文最佳;

feign

当我们引入spring-cloud-openfeign-core的时候,会引入org.springframework.cloud.openfeign.ribbon.DefaultFeignLoadBalancedConfiguration这个配置文件,这个配置文件提供了一个feign.Client接口的实现org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClientfeign.Client接口是feign中最核心的接口,feign框架的所有网络请求最终都会统一调用feign.Client,具体网络请求如何发起feign本身并不关心,我们可以自己实现网络请求,当然,feign中也给我们默认实现了一些,比如feign.Client.Defaultfeign.httpclient.ApacheHttpClient等,底层使用了不同的网络框架来处理网络请求,默认的实现是feign.Client.Default,这个实现中使用了jdk自带的java.net.HttpURLConnection实现了网络请求,没有连接池等概念,每次请求都会新建连接,效率比较低,不过这不在我们讨论的重点;

本文不重点讨论feign的网络实现,不过如果项目中有使用feign的话,要关注这点,一定要替代默认实现,默认实现的性能较差;

ribbon

虽然feign默认的实现是feign.Client.Default,但是实际上feign框架并没有直接使用该实现,而是使用了org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient包装了一层,我们的请求会被LoadBalancerFeignClient委托到com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(S, com.netflix.client.config.IClientConfig)方法处,该方法将请求提交到了com.netflix.loadbalancer.reactive.LoadBalancerCommand中,最终目的就是使用ribbon的负载均衡能力决策出一个com.netflix.loadbalancer.Server,而该Server就是为我们本次请求提供服务的服务端,包含ip、端口号等;

从这里也可以看出,默认情况下feign和ribbon是强绑定的;

下面我们来分析Server是如何决策出来的,首先是LoadBalancerCommand中提供了com.netflix.loadbalancer.reactive.LoadBalancerCommand#selectServer方法来选择为当前请求提供服务的ServerselectServer方法并没有直接实现该逻辑,而是将其委托到了com.netflix.loadbalancer.LoadBalancerContext#getServerFromLoadBalancer处,该方法中又将Server的决策逻辑委托到了com.netflix.loadbalancer.ILoadBalancer#chooseServer

com.netflix.loadbalancer.ILoadBalancer是何时注入的呢?我们回到org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute处,在这里,调用了lbClient方法来构建了feign负载均衡FeignLoadBalancer的实例,lbClient方法中将构建委托给了org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory#create方法,最终在该方法中构建了FeignLoadBalancer的实例,该方法中通过调用org.springframework.cloud.netflix.ribbon.SpringClientFactory#getLoadBalancer来构建了com.netflix.loadbalancer.ILoadBalancer实例,getLoadBalancer方法中通过调用org.springframework.cloud.context.named.NamedContextFactory#getInstance(java.lang.String, java.lang.Class<T>)以获取bean的方式获取到了ILoadBalancer实例,并将其注入了FeignLoadBalancer实例中;

实际上SpringClientFactory继承自org.springframework.cloud.context.named.NamedContextFactory,是ribbon自定义的NamedContextFactory,这是spring cloud context组件提供的一个工厂类,用于创建和管理具有名称的应用程序上下文;

上个问题解决了,但是现在又有了新问题,org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory中的org.springframework.cloud.netflix.ribbon.SpringClientFactory实例又是在哪儿构建的呢?ILoadBalancer类型的bean又是在哪儿声明的呢?

要回答这个问题,就要引入ribbon组件了,当我们引入spring-cloud-netflix-ribbon的时候,org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration就会被自动引入,该配置中声明了SpringClientFactory这个bean,同时该配置上添加了@RibbonClients注解,该注解引入了org.springframework.cloud.netflix.ribbon.RibbonClientConfigurationRegistrar,这个bean最终会扫描org.springframework.cloud.netflix.ribbon.RibbonClients注解和org.springframework.cloud.netflix.ribbon.RibbonClient注解来生成注册一批org.springframework.cloud.netflix.ribbon.RibbonClientSpecificationbean定义,最终这些RibbonClientSpecification会被注入到我们构建的SpringClientFactory中作为配置,而SpringClientFactory的默认配置则是org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration,该配置中声明了许多bean,其中就包含ILoadBalancer这个bean;

eureka

上边提到了feignribbon是如何协作的,那feignribbon又是如何与eureka协作的呢?核心就在于ribbon提供的默认ILoadBalancer的实现com.netflix.loadbalancer.ZoneAwareLoadBalancer,在ZoneAwareLoadBalancerchooseServer实现中,实际上并没有实现具体的逻辑,具体的逻辑是委托给了com.netflix.loadbalancer.IRule#choose,而IRule这个bean在RibbonClientConfiguration配置类中也有,提供了一个默认实现com.netflix.loadbalancer.ZoneAvoidanceRule,不过ZoneAvoidanceRulechoose方法中又先通过ILoadBalancergetAllServers获取了所有Server列表,然后根据相关算法从里边挑处了一个Server,不过我们对这些算法不关心,我们关心的是它如何对接上了eureka的服务发现的能力;

现在我们继续回到ILoadBalancer中,ribbon提供的默认ILoadBalancer实现ZoneAwareLoadBalancer中通过com.netflix.loadbalancer.ServerList来获取了所有Server列表,而ribboneureka协作的重点就在于ServerList上;

当我们引入spring-cloud-netflix-eureka-client的时候,org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration会被自动引入,同样的,该配置类上添加了@RibbonClients注解,与RibbonAutoConfiguration不同的是,该注解还指定了使用org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientConfiguration配置类 ,而不是使用默认的配置类,这个配置类中声明了一个很重要的bean,那就是com.netflix.loadbalancer.ServerList这个bean,这个bean替代了ribbon默认的ServerList实现,其中使用了EurekaClient来获取指定服务的所有服务提供方ip、端口等信息,这样,ribbon就能使用到eureka提供的服务发现的能力了;

总结

看到上边绕来绕去,是不是感觉脑瓜嗡嗡叫?

简单总结下,feignribbon通过org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient关联了起来,ribbonfeign提供了负载均衡的能力,而eureka则通过org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList实现了ribboncom.netflix.loadbalancer.ServerList接口,来为ribbon提供服务发现能力;

至此,feignribboneureka的协作原理我们已经解析完毕,其中还有很多细节没有讲到,读者可以自行阅读源码来细细品味,例如ribbon的负载均衡策略实现算法、ribbon是如何对服务发现给到的后端服务进行健康检查的、feign网络请求的几种内置实现、eureka的分区负载均衡、NamedContextFactoryribbon中是如何使用的等等;

联系我

  • 作者微信:JoeKerouac
  • 微信公众号(文章会第一时间更新到公众号,如果搜不出来可能是改名字了,加微信即可=_=|):代码深度研究院
  • GitHub:https://github.com/JoeKerouac

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: feign-eureka-ribbon的协作原理

  1. 你写得非常清晰明了,让我很容易理解你的观点。

  1. 暂无 Trackback

return top