ribbon 代码结构
首先看核心接口ILoadBalancer
分成几个核心的接口方法定义
addServers(List)
向负载均衡器中维护的实例列表增加服务实例
chooseServer(Object)
通过某种策略,从负载均衡器中挑选出一个具体的服务实例
markServerDown(Server)
用来通知和标识负载均衡器中某个具体实例已经停止服务,不然负载均衡器在下一次获取服务实例清单前都会认为服务实例均是正常服务的
getReachableServers()
返回当前可正常服务的实例列表
getAllServers()
返回所有已知的服务实例列表,包括正常服务和停止服务的实例
在该接口中涉及的Server对象定义是一个传统的服务端节点,在其中存储了服务端节点的一些基础信息,如host\port等。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
看具体的实现类zoneAwareLoadBalancer
在该抽象类中定义了一个关于服务实例的分组枚举类ServerGroup,它包含三种不同的类型。
ALL: 所有服务实例
STATUS_UP: 正服务的实例
STATUS_NOT_UP: 停止服务的实例
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
继续看子类实现BaseLoadBalancer
实现了接口的方法声明
1. addServers,调用setServersList
1.1 获取写锁writeLock,然后调用lock()加锁
1.2 增加到allServers这个列表,如果已经存在,则通过比对后更新
2.核心方法chooseServer的实现
2.1 首先初始化counter计数器,没有就创建
2.2 计数器累加,调用increment
2.3 判断rule,为空就直接返回null
2.4 调用rule.choose(key)来选择
AvailabilityFilteringRule
该策略继承自抽象策略PredicateBasedRule ,也继承了"先过滤清单,再轮询选择"的基本处理逻辑;该策略通过线性抽样的方式直接尝试可用且较空闲的实例来使用,优化了父类每次都要遍历所有实例的开销。超过10次还选不到,就使用父类策略。
再深入看下 AvailabilityPredicate
1.获取负载均衡状态,如果为null,就直接返回true
2.如果断路器打开,满足条件,则需要过滤
3.如果实例的并发请求数大于阈值,则需要过滤
BestAvailableRule
选择最小请求数的Server
RandomRule
随机选择
RoundRobinRule
线性轮询
RetryRule
根据轮询的方式重试
WeightedResponseTimeRule
根据响应时间去分配Weight,Weight越高,被选择的可能性就越大
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DynamicServerListLoadBalancer
DynamicServerListLoadBalancer继承于BaseLoadBalancer,它对基础负载均衡器做了扩展。在该负载均衡器,实现了服务实例清单在运行期间的动态更新;同时还具备了对服务实例清单的过滤功能,也就是说可以通过过滤器来选择获取一批服务实例清单。
DynamicServerListLoadBalancer默认的策略,它是通过定时任务的方式进行服务列表的更新
实现逻辑主要依靠EurekaClient从注册中心获取服务实例(instanceInfo)列表,然后遍历这些服务实例,将状态为UP(正常服务)的实例转换成DiscoverEnabledServer对象,最后组织成列表返回。
它的实现类有两个
PollingServerListUpdater
DynamicServerListLoadBalancer默认的策略,它是通过定时任务的方式进行服务列表的更新
EurekaNotificationServerListUpdater
它需要利用Eureka的事件监听器来驱动服务列表的更新操作
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
再看下核心的服务过滤接口ServerListFilter
该接口很简单,只有一个方法List getFilteredListOfServers(List),它主要用于实现服务实例列表的过滤,通过传入服务实例清单,根据一些规则返回过滤后的服务实例清单。
下面看下具体实现类 ZoneAffinityServerListFilter
区域感知过滤器
该过滤器基于”区域感知”(Zone Affinity)的方式实现服务实例的过滤,它会根据提供服务的实例所在的区域(Zone)与消费者自身所在的区域(Zone)进行比较,过滤掉那些不是在同一区域的实例。
需要注意的是,该过滤器还会通过shouldEnableZoneAffinity(List)方法来判断是否要启用”区域感知”的功能,它使用了LoadBalancerStats的getZoneSnapshot方法来获取这些过滤后的同区域实例的基础指标(包含实例数、断路器断开数、清动请求数、实例平均负载等),根据一系列的算法得出下面同筱评价值并与阈值进行对比,若有一个条件符合,就不启用”区域感知”过滤后的服务实例清单。目的是集群出现区域故障时,依然可以依靠其它区域的正常实例提供服务,保障高可用。
1、故障实例百分比(断路器断开数/实例数)>=0.8
2、实例平均负载>=0.6
3、可用实例数(实例数-数路器断开数)<2
ServerListSubsetFilter
该过滤器适用于大规模服务器集群(上百或更多)的系统,因为它可以产生一个”区域感知”结果的子集列表,同时它还能够通过比较服务实例的通信失败数和并发连接数来判定该服务是否健康来选择性的从服务实例列表中剔除那些相对不够健康的实例
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
核心负载均衡实现ZoneAwareLoadBalancer
1.如果没有启用区域感知,或者可用的区域zone小于等于1个,那么则使用父类的负载均衡方法
2.获取负载均衡的stats,创建区域快照 zoneSnapshot
3.然后获取可用的区域availableZones
4.随机选择一个zone,然后获取这个区域zone的负载均衡器zoneLoadBalancer
5.根据这个负载均衡器选择对应的server