上一讲深刻的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList
,同时在DynamicServerListLoadBalancer
中会调用PollingServerListUpdater
进行定时更新Eureka注册表信息到BaseLoadBalancer
中,默认30s调度一次。java
咱们知道Ribbon主要是由3个组件组成的:算法
其中ILoadBalancer
前面咱们已经分析过了,接下来咱们一块儿看看IRule
和IPing
中的具体实现。spring
目录以下:服务器
原创不易,如若转载 请标明来源!微信
博客地址:一枝花算不算浪漫
微信公众号:壹枝花算不算浪漫并发
还记得咱们上一讲说过,在Ribbon初始化过程当中,默认的IRule
为ZoneAvoidanceRule
,这里咱们能够经过debug看看,从RibbonLoadBalancerClient.getServer()
一路往下跟,这里直接看debug结果:app
而后咱们继续跟ZoneAvoidanceRule.choose()
方法:负载均衡
public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule { /** * Method that provides an instance of {@link AbstractServerPredicate} to be used by this class. * */ public abstract AbstractServerPredicate getPredicate(); /** * Get a server by calling {@link AbstractServerPredicate#chooseRandomlyAfterFiltering(java.util.List, Object)}. * The performance for this method is O(n) where n is number of servers to be filtered. */ @Override public Server choose(Object key) { ILoadBalancer lb = getLoadBalancer(); Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key); if (server.isPresent()) { return server.get(); } else { return null; } } }
这里是调用的ZoneAvoidanceRule
的父类中的choose()
方法,首先是拿到对应的ILoadBalancer
,而后拿到对应的serverList数据,接着调用chooseRoundRobinAfterFiltering()
方法,继续日后跟:dom
public abstract class AbstractServerPredicate implements Predicate<PredicateKey> { public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) { List<Server> eligible = getEligibleServers(servers, loadBalancerKey); if (eligible.size() == 0) { return Optional.absent(); } return Optional.of(eligible.get(incrementAndGetModulo(eligible.size()))); } private int incrementAndGetModulo(int modulo) { for (;;) { int current = nextIndex.get(); int next = (current + 1) % modulo; if (nextIndex.compareAndSet(current, next) && current < modulo) return current; } } }
到了这里能够看到incrementAndGetModulo()
方法就是处理serverList轮询的算法,这个和RoudRobinRule
中采用的是同样的算法,这个算法你们能够品一品,我这里也会画个图来讲明下:ide
看了图=中的例子估计你们都会明白了,这个算法就是依次轮询。这个算法写的很精简。
咱们上面知道,咱们按照轮询的方式从serverList取到一个server后,那么怎么把以前原有的相似于:http://ServerA/sayHello/wangmeng
中的ServerA给替换成请求的ip数据呢?
接着咱们继续看RibbonLoadBalancerClient.execute()
方法,这个里面request.apply()
会作一个serverName的替换逻辑。
最后能够一步步跟到RibbonLoadBalancerClient.reconstructURI()
,这个方法是把请求自带的getURI方法给替换了,咱们最后查看context.reconstructURIWithServer()
方法,debug结果如图,这个里面会一步步把对应的请求url给拼接起来:
咱们知道 Ribbon还有一个重要的组件就是ping机制,经过上一讲Ribbon的初始化咱们知道,默认的IPing实现类为:NIWSDiscoveryPing
,咱们能够查看其中的isAlive()
方法:
public class NIWSDiscoveryPing extends AbstractLoadBalancerPing { BaseLoadBalancer lb = null; public NIWSDiscoveryPing() { } public BaseLoadBalancer getLb() { return lb; } /** * Non IPing interface method - only set this if you care about the "newServers Feature" * @param lb */ public void setLb(BaseLoadBalancer lb) { this.lb = lb; } public boolean isAlive(Server server) { boolean isAlive = true; if (server!=null && server instanceof DiscoveryEnabledServer){ DiscoveryEnabledServer dServer = (DiscoveryEnabledServer)server; InstanceInfo instanceInfo = dServer.getInstanceInfo(); if (instanceInfo!=null){ InstanceStatus status = instanceInfo.getStatus(); if (status!=null){ isAlive = status.equals(InstanceStatus.UP); } } } return isAlive; } @Override public void initWithNiwsConfig( IClientConfig clientConfig) { } }
这里就是获取到DiscoveryEnabledServer
对应的注册信息是否为UP
状态。那么 既然有个ping的方法,确定会有方法进行调度的。
咱们能够查看isAlive()
调用便可以找到调度的地方。
在BaseLoadBalancer
构造函数中会调用setupPingTask()
方法,进行调度:
protected int pingIntervalSeconds = 10; void setupPingTask() { if (canSkipPing()) { return; } if (lbTimer != null) { lbTimer.cancel(); } lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name, true); lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000); forceQuickPing(); }
这里pingIntervalSeconds
在BaseLoadBalancer
中定义的是10s,可是在initWithConfig()
方法中,经过传入的时间覆盖了本来的10s,这里实际的默认时间是30s。以下代码:
咱们也能够经过debug来看看:
可能你们好奇为何要单独截图来讲这个事,实际上是由于网上好多博客讲解都是错的,都写的是ping默认调度时间为10s,想必他们都是没有真正debug过吧。
仍是那句话,源码出真知。
RoundRobinRule:系统内置的默认负载均衡规范,直接round robin轮询,从一堆server list中,不断的轮询选择出来一个server,每一个server平摊到的这个请求,基本上是平均的
这个算法,说白了是轮询,就是一台接着一台去请求,是按照顺序来的
AvailabilityFilteringRule:这个rule就是会考察服务器的可用性
若是3次链接失败,就会等待30秒后再次访问;若是不断失败,那么等待时间会不断变长
若是某个服务器的并发请求过高了,那么会绕过去,再也不访问
这里先用round robin算法,轮询依次选择一台server,若是判断这个server是存活的可用的,若是这台server是不能够访问的,那么就用round robin算法再次选择下一台server,依次循环往复,10次。
WeightedResponseTimeRule:带着权重的,每一个服务器能够有权重,权重越高优先访问,若是某个服务器响应时间比较长,那么权重就会下降,减小访问
ZoneAvoidanceRule:根据机房和服务器来进行负载均衡,说白了,就是机房的意思,看了源码就是知道了,这个就是所谓的spring cloud ribbon环境中的默认的Rule
BestAvailableRule:忽略那些请求失败的服务器,而后尽可能找并发比较低的服务器来请求
到了这里 Ribbon相关的就结束了,对于Ribbon注册表拉取及更新逻辑这里也梳理下,这里若是Ribbon保存的注册表信息有宕机的状况,这里最多4分钟才能感知到,因此spring cloud还有一个服务熔断的机制,这个后面也会讲到。
本文章首发自本人博客:https://www.cnblogs.com/wang-meng 和公众号:壹枝花算不算浪漫,如若转载请标明来源!
感兴趣的小伙伴可关注我的公众号:壹枝花算不算浪漫