1. Ribbon 负债均衡策略
| 命名 | 描述 | |
|---|---|---|
| RandomRule | 随机策略 | 随机选择server |
| RoundRobinRule | 轮询策略 | 按照顺序选择server(ribbon默认策略) |
| RetryRule | 重试策略 | 在一个配置时间段内,当选择server不成功,则一直尝试选择一个可用的server |
| BestAvailableRule | 最低并发策略 | 逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server |
| AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值) |
| ResponseTimeWeightedRule | 响应时间加权重策略 | 根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间 |
| ZoneAvoidanceRule | 区域权重策略 | 综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server |
2. 源码分析
负载均衡的策略的核心实现接口是IRule 接口中的choose()方法,不同的负载策略通过实现choose()方法来实现不同逻辑
public interface IRule{/** choose one alive server from lb.allServers or* lb.upServers according to key** @return choosen Server object. NULL is returned if none* server is available*/public Server choose(Object key);public void setLoadBalancer(ILoadBalancer lb);public ILoadBalancer getLoadBalancer();}
2.1. 随机策略
随机策略的实现类是RandomRule类,实现choose()方法
public Server choose(ILoadBalancer lb, Object key) {// 传入的LoadBalancer是空,直接返回nullif (lb == null) {return null;}Server server = null;// 如果没有获取到随机服务,一直循环直到获取到服务while (server == null) {// 线程如果被中断,返回nullif (Thread.interrupted()) {return null;}// 所有已注册可用服务列表List<Server> upList = lb.getReachableServers();// 所有已注册服务列表List<Server> allList = lb.getAllServers();// 所有已注册服务数量int serverCount = allList.size();if (serverCount == 0) {/** No servers. End regardless of pass, because subsequent passes* only get more restrictive.*/return null;}// 关键方法,生成随机一个0--serverCount 之间的随机数作为下标int index = chooseRandomInt(serverCount);// 根据下表从可用服务列表中拿Server实例server = upList.get(index);// server 为空,这种情况是因为serverList在这个时候正在被修正if (server == null) {/** The only time this should happen is if the server list were* somehow trimmed. This is a transient condition. Retry after* yielding.*/// 通知线程调度器,让出当前CPU// 这里的yield,是和上面第10行代码配合使用的,这是一种防御性编程的体现Thread.yield();continue;}// 如果server是可用的直接返回if (server.isAlive()) {return (server);}// Shouldn't actually happen.. but must be transient or a bug.server = null;Thread.yield();}return server;}
生成随机数,使用ThreadLocalRandom 然后在当前的thread中通过nextInt传入了一个serverCount,然后就会返回0到serverCount中的任意一个值。
// 生成随机数protected int chooseRandomInt(int serverCount) {return ThreadLocalRandom.current().nextInt(serverCount);}
2.2. 轮询策略
轮询策略实现类RoundRobinRule
public Server choose(ILoadBalancer lb, Object key) {// 如果没有LoadBalancer直接返回nullif (lb == null) {log.warn("no load balancer");return null;}Server server = null;int count = 0;// 循环获取可用服务,直到获取服务和获取次数在10次以下结束循环while (server == null && count++ < 10) {// 可用的注册服务List<Server> reachableServers = lb.getReachableServers();// 所有的注册服务List<Server> allServers = lb.getAllServers();// 可用服务数量int upCount = reachableServers.size();// 所有服务数量int serverCount = allServers.size();if ((upCount == 0) || (serverCount == 0)) {log.warn("No up servers available from load balancer: " + lb);return null;}// 获取轮询服务下标int nextServerIndex = incrementAndGetModulo(serverCount);server = allServers.get(nextServerIndex);// 服务为null时,让出CPUif (server == null) {/* Transient. */Thread.yield();continue;}// 校验服务可用时直接返回if (server.isAlive() && (server.isReadyToServe())) {return (server);}// Next.server = null;}// 利用计数器限制循环次数,如果10次还没有获取到可用服务,打印一行日志if (count >= 10) {log.warn("No available alive servers after 10 tries from load balancer: "+ lb);}return server;}
生成轮询服务下标
private int incrementAndGetModulo(int modulo) {// 自选的方式获取轮询下标for (;;) {// 原子操作,上一次被调用的服务下标int current = nextServerCyclicCounter.get();// 上一次访问机器的下标+1和服务器数量相除取余,得到当前的服务下标int next = (current + 1) % modulo;// 通过CAS对比替换上一次调用服务的值为next值if (nextServerCyclicCounter.compareAndSet(current, next))return next;}}
