深入理解Ribbon之源码解析
什么是Ribbon
Ribbon是Netflix公司开源的一个负载均衡的项目,它属于上述的第二种,是一个客户端负载均衡器,运行在客户端上。它是一个经过了云端测试的IPC库,可以很好地控制HTTP和TCP客户端的一些行为。 Feign已经默认使用了Ribbon。
负载均衡
容错
多协议(HTTP,TCP,UDP)支持异步和反应模型
缓存和批处理
RestTemplate和Ribbon相结合
Ribbon在Netflix组件是非常重要的一个组件,在Zuul中使用Ribbon做负载均衡,以及Feign组件的结合等。在Spring Cloud 中,作为开发中,做的最多的可能是将RestTemplate和Ribbon相结合,你可能会这样写:
@Configuration
public class RibbonConfig { @Bean@LoadBalancedRestTemplate restTemplate() { return new RestTemplate();}
}
消费另外一个的服务的接口,差不多是这样的:
@Servicepublic class RibbonService { @AutowiredRestTemplate restTemplate;public String hi(String name) { return restTemplate.getForObject("http://eureka-client/hi?name="+name,String.class);}
}
深入理解Ribbon
LoadBalancerClient
在Riibon中一个非常重要的组件为LoadBalancerClient,它作为负载均衡的一个客户端。它在spring-cloud-commons包下: 的LoadBalancerClient是一个接口,它继承ServiceInstanceChooser,它的实现类是RibbonLoadBalancerClient,这三者之间的关系如下图:
其中LoadBalancerClient接口,有如下三个方法,其中excute()为执行请求,reconstructURI()用来重构url:
|
|
ServiceInstanceChooser接口,主要有一个方法,用来根据serviceId来获取ServiceInstance,代码如下:
|
|
LoadBalancerClient的实现类为RibbonLoadBalancerClient,这个类是非常重要的一个类,最终的负载均衡的请求处理,由它来执行。它的部分源码如下:
|
|
在RibbonLoadBalancerClient的源码中,其中choose()方法是选择具体服务实例的一个方法。该方法通过getServer()方法去获取实例,经过源码跟踪,最终交给了ILoadBalancer类去选择服务实例。
ILoadBalancer在ribbon-loadbalancer的jar包下,它是定义了实现软件负载均衡的一个接口,它需要一组可供选择的服务注册列表信息,以及根据特定方法去选择服务,它的源码如下 :
|
|
其中,addServers()方法是添加一个Server集合;chooseServer()方法是根据key去获取Server;markServerDown()方法用来标记某个服务下线;getReachableServers()获取可用的Server集合;getAllServers()获取所有的Server集合。
DynamicServerListLoadBalancer
它的继承类为BaseLoadBalancer,它的实现类为DynamicServerListLoadBalancer,这三者之间的关系如下:
查看上述三个类的源码,可用发现,配置以下信息,IClientConfig、IRule、IPing、ServerList、ServerListFilter和ILoadBalancer,查看BaseLoadBalancer类,它默认的情况下,实现了以下配置:
IClientConfig ribbonClientConfig: DefaultClientConfigImpl配置
IRule ribbonRule: RoundRobinRule 路由策略
IPing ribbonPing: DummyPing
ServerList ribbonServerList: ConfigurationBasedServerList
ServerListFilter ribbonServerListFilter: ZonePreferenceServerListFilter
ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
IClientConfig 用于对客户端或者负载均衡的配置,它的默认实现类为DefaultClientConfigImpl。
IRule用于复杂均衡的策略,它有三个方法,其中choose()是根据key 来获取server,setLoadBalancer()和getLoadBalancer()是用来设置和获取ILoadBalancer的,它的源码如下:
|
|
IRule有很多默认的实现类,这些实现类根据不同的算法和逻辑来处理负载均衡。Ribbon实现的IRule有一下。在大多数情况下,这些默认的实现类是可以满足需求的,如果有特性的需求,可以自己实现。
BestAvailableRule 选择最小请求数
ClientConfigEnabledRoundRobinRule 轮询
RandomRule 随机选择一个server
RoundRobinRule 轮询选择server
RetryRule 根据轮询的方式重试
WeightedResponseTimeRule 根据响应时间去分配一个weight ,weight越低,被选择的可能性就越低
ZoneAvoidanceRule 根据server的zone区域和可用性来轮询选择
-
1 2 3
public interface IPing { public boolean isAlive(Server server); }
IPing是用来想server发生”ping”,来判断该server是否有响应,从而判断该server是否可用。它有一个isAlive()方法,它的源码如下:
IPing的实现类有PingUrl、PingConstant、NoOpPing、DummyPing和NIWSDiscoveryPing。它门之间的关系如下:
PingUrl 真实的去ping 某个url,判断其是否alive
PingConstant 固定返回某服务是否可用,默认返回true,即可用
NoOpPing 不去ping,直接返回true,即可用。
DummyPing 直接返回true,并实现了initWithNiwsConfig方法。
NIWSDiscoveryPing,根据DiscoveryEnabledServer的InstanceInfo的InstanceStatus去判断,如果为InstanceStatus.UP,则为可用,否则不可用。
ServerList是定义获取所有的server的注册列表信息的接口,它的代码如下:
|
|
ServerListFilter接口,定于了可根据配置去过滤或者根据特性动态获取符合条件的server列表的方法,代码如下:
|
|
阅读DynamicServerListLoadBalancer的源码,DynamicServerListLoadBalancer的构造函数中有个initWithNiwsConfig()方法。在改方法中,经过一系列的初始化配置,最终执行了restOfInit()方法。其代码如下:
在restOfInit()方法上,有一个 updateListOfServers()的方法,该方法是用来获取所有的ServerList的。
在restOfInit()方法上,有一个 updateListOfServers()的方法,该方法是用来获取所有的ServerList的。
进一步跟踪updateListOfServers()方法的源码,最终由serverListImpl.getUpdatedListOfServers()获取所有的服务列表的,代码如下:
而serverListImpl是ServerList接口的具体实现类。跟踪代码,ServerList的实现类为DiscoveryEnabledNIWSServerList,在ribbon-eureka.jar的com.netflix.niws.loadbalancer下。其中DiscoveryEnabledNIWSServerList有 getInitialListOfServers()和getUpdatedListOfServers()方法,具体代码如下:
继续跟踪源码,obtainServersViaDiscovery(),是根据eurekaClientProvider.get()来回去EurekaClient,再根据EurekaClient来获取注册列表信息,代码如下:
其中eurekaClientProvider的实现类是LegacyEurekaClientProvider,它是一个获取eurekaClient类,通过静态的方法去获取eurekaClient,其代码如下:
EurekaClient的实现类为DiscoveryClient,在之前已经分析了它具有服务注册、获取服务注册列表等的全部功能。
由此可见,负载均衡器是从EurekaClient获取服务信息,并根据IRule去路由,并且根据IPing去判断服务的可用性。
那么现在还有个问题,负载均衡器多久一次去获取一次从Eureka Client获取注册信息呢。
在BaseLoadBalancer类下,BaseLoadBalancer的构造函数,该构造函数开启了一个PingTask任务,代码如下:
setupPingTask()的具体代码逻辑,它开启了ShutdownEnabledTimer执行PingTask任务,在默认情况下pingIntervalSeconds为10,即每10秒钟,想EurekaClient发送一次”ping”。
PingTask源码,即new一个Pinger对象,并执行runPinger()方法。
查看Pinger的runPinger()方法,最终根据 pingerStrategy.pingServers(ping, allServers)来获取服务的可用性,如果该返回结果,如之前相同,则不去向EurekaClient获取注册列表,如果不同则通知ServerStatusChangeListener或者changeListeners发生了改变,进行更新或者重新拉取。
由此可见,LoadBalancerClient是在初始化的时候,会向Eureka回去服务注册列表,并且向通过10s一次向EurekaClient发送“ping”,来判断服务的可用性,如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。LoadBalancerClient有了这些服务注册列表,就可以根据具体的IRule来进行负载均衡。
RestTemplate是如何和Ribbon结合的
最后,回答问题的本质,为什么在RestTemplate加一个@LoadBalance注解就可可以开启负载均衡呢?
全局搜索ctr+shift+f @LoadBalanced有哪些类用到了LoadBalanced有哪些类用到了, 发现LoadBalancerAutoConfiguration类,即LoadBalancer自动配置类。
在该类中,首先维护了一个被@LoadBalanced修饰的RestTemplate对象的List,在初始化的过程中,通过调用customizer.customize(restTemplate)方法来给RestTemplate增加拦截器LoadBalancerInterceptor。
而LoadBalancerInterceptor,用于实时拦截,在LoadBalancerInterceptor这里实现来负载均衡。LoadBalancerInterceptor的拦截方法如下:
总结
综上所述,Ribbon的负载均衡,主要通过LoadBalancerClient来实现的,而LoadBalancerClient具体交给了ILoadBalancer来处理,ILoadBalancer通过配置IRule、IPing等信息,并向EurekaClient获取注册列表的信息,并默认10秒一次向EurekaClient发送“ping”,进而检查是否更新服务列表,最后,得到注册列表后,ILoadBalancer根据IRule的策略进行负载均衡。
而RestTemplate 被@LoadBalance注解后,能过用负载均衡,主要是维护了一个被@LoadBalance注解的RestTemplate列表,并给列表中的RestTemplate添加拦截器,进而交给负载均衡器去处理。
深入理解Ribbon之源码解析相关推荐
- 彻底理解OkHttp - OkHttp 源码解析及OkHttp的设计思想
OkHttp 现在统治了Android的网络请求领域,最常用的框架是:Retrofit+okhttp.OkHttp的实现原理和设计思想是必须要了解的,读懂和理解流行的框架也是程序员进阶的必经之路,代码 ...
- 深入理解Eureka之源码解析
Eureka的一些概念 Register:服务注册 当Eureka客户端向Eureka Server注册时,它提供自身的元数据,比如IP地址.端口,运行状况指示符URL,主页等. Renew:服务续约 ...
- 深入理解Zuul之源码解析
转载请标明出处: 原文首发于https://www.fangzhipeng.com/springcloud/2017/09/12/sc-zuul-raw/ 本文出自方志朋的博客 Zuul 架构图 在z ...
- JQuery 源码解析资料
2019独角兽企业重金招聘Python工程师标准>>> jQuery源码分析系列目录 jQuery源码解读-理解架构 jQuery源码解析(架构与依赖模块) jQuery v1.10 ...
- Volley 源码解析之网络请求
Volley源码分析三部曲 Volley 源码解析之网络请求 Volley 源码解析之图片请求 Volley 源码解析之缓存机制 Volley 是 Google 推出的一款网络通信框架,非常适合数据量 ...
- jQuery源码解析(架构与依赖模块)第一章 理解架构
1-1 jQuery设计理念 引用百科的介绍: jQuery是继prototype之后又一个优秀的Javascript框架.它是轻量级的js库 ,它兼容CSS3,还兼容各种浏览器(IE 6.0+, F ...
- Block源码解析和深入理解
Block源码解析和深入理解 Block的本质 Block是"带有自动变量值的匿名函数". 我们通过Clang(LLVM编译器)来将OC的代码转换成C++源码的形式,通过如下命令: ...
- 详细讲解go web框架之gin框架源码解析记录及思路流程和理解
开篇 首先gin 框架是在 官方提供的net/http标准包进行的相应封装. 那么要想理解gin框架, 就要先懂一些 net/http标准包 的相关知识. 可以参考中文的 文档: https://st ...
- 【详解】Ribbon 负载均衡服务调用原理及默认轮询负载均衡算法源码解析、手写
Ribbon 负载均衡服务调用 一.什么是 Ribbon 二.LB负载均衡(Load Balancer)是什么 1.Ribbon 本地负载均衡客户端 VS Nginx 服务端负载均衡的区别 2.LB负 ...
最新文章
- 翻遍用AI检测新冠的论文,一篇临床可用的也没有?!
- 三星samsung手机ROM制作教程-另外一篇
- 工业大数据的真正意义和价值
- iphone NSArray 数组越界
- LeetCode 187. 重复的DNA序列(哈希/位运算)
- ATL offsetofclass 的工作原理
- Java的Stack类
- 数据分析的5层解读,报表仍是有效的落地实践!
- 【深度学习之美】激活引入非线性,池化预防过拟合(入门系列之十二)
- 在Pandas中直接加载MongoDB的数据
- Pandas DataFrame索引和列属性
- oracle补丁类型
- C#cmd执行命令隐藏窗口,并保持程序一直运行
- 微信小程序开发04-打造自己的UI库
- 转载:完全卸载SQL Server 2005的方法
- 为什么抖音网红城市都在西部?
- Directx11代码下载
- LeetCode 460. LFU缓存
- Python 基础知识自检,你离深入掌握 Python 还有多远
- 凡人修真3D(3)神翼
热门文章
- main方法为什么是静态的
- 在一个数组中查找两个重复出现的数字
- 如何成为一个好的测试工程师(转载,大意)
- device.cpp
- C# 文件操作(上传 下载 删除 文件列表...)
- 【Whalepaper】NLP论文研读 - Keyword-Attentive Deep Semantic Matching
- TIOBE 新榜单:Python 超越 Java 重回第二,Rust 崛起
- AV1时代要来了,超高清视频时代视频编码技术的机遇与挑战
- Pytorch和Tensorflow,谁会笑到最后?
- 为什么华为200万招聘AI博士,马斯克却推出脑机接口对抗AI?