为什么80%的码农都做不了架构师?>>>   

本文主要研究一下RibbonLoadBalancerClient的choose方法

RibbonLoadBalancerClient.choose

spring-cloud-netflix-ribbon-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java

   public ServiceInstance choose(String serviceId) {Server server = getServer(serviceId);if (server == null) {return null;}return new RibbonServer(serviceId, server, isSecure(server, serviceId),serverIntrospector(serviceId).getMetadata(server));}protected ILoadBalancer getLoadBalancer(String serviceId) {return this.clientFactory.getLoadBalancer(serviceId);}protected Server getServer(String serviceId) {return getServer(getLoadBalancer(serviceId));}protected Server getServer(ILoadBalancer loadBalancer) {if (loadBalancer == null) {return null;}return loadBalancer.chooseServer("default"); // TODO: better handling of key}

可以看到,choose方法传入serviceId,然后通过SpringClientFactory获取ILoadBalancer,最后调用ILoadBalancer.chooseServer方法选取Server 这里的key,2.0.0.RC1版本直接写死default了。

ILoadBalancer.chooseServer

ribbon-loadbalancer-2.2.5-sources.jar!/com/netflix/loadbalancer/ILoadBalancer.java

  /*** Choose a server from load balancer.* * @param key An object that the load balancer may use to determine which server to return. null if *         the load balancer does not use this parameter.* @return server chosen*/public Server chooseServer(Object key);

ZoneAwareLoadBalancer

ribbon-loadbalancer-2.2.5-sources.jar!/com/netflix/loadbalancer/ZoneAwareLoadBalancer.java

/*** Load balancer that can avoid a zone as a whole when choosing server. *<p>* The key metric used to measure the zone condition is Average Active Requests,
which is aggregated per rest client per zone. It is the
total outstanding requests in a zone divided by number of available targeted instances (excluding circuit breaker tripped instances).
This metric is very effective when timeout occurs slowly on a bad zone.
<p>
The  LoadBalancer will calculate and examine zone stats of all available zones. If the Average Active Requests for any zone has reached a configured threshold, this zone will be dropped from the active server list. In case more than one zone has reached the threshold, the zone with the most active requests per server will be dropped.
Once the the worst zone is dropped, a zone will be chosen among the rest with the probability proportional to its number of instances.
A server will be returned from the chosen zone with a given Rule (A Rule is a load balancing strategy, for example {@link AvailabilityFilteringRule})
For each request, the steps above will be repeated. That is to say, each zone related load balancing decisions are made at real time with the up-to-date statistics aiding the choice.* @author awang** @param <T>*/
public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {private static final DynamicBooleanProperty ENABLED = DynamicPropertyFactory.getInstance().getBooleanProperty("ZoneAwareNIWSDiscoveryLoadBalancer.enabled", true);//......public Server chooseServer(Object key) {if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {logger.debug("Zone aware logic disabled or there is only one zone");return super.chooseServer(key);}Server server = null;try {LoadBalancerStats lbStats = getLoadBalancerStats();Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);logger.debug("Zone snapshots: {}", zoneSnapshot);if (triggeringLoad == null) {triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);}if (triggeringBlackoutPercentage == null) {triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);}Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());logger.debug("Available zones: {}", availableZones);if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);logger.debug("Zone chosen: {}", zone);if (zone != null) {BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);server = zoneLoadBalancer.chooseServer(key);}}} catch (Exception e) {logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);}if (server != null) {return server;} else {logger.debug("Zone avoidance logic is not invoked.");return super.chooseServer(key);}}
}

这里ZoneAwareNIWSDiscoveryLoadBalancer.enabled默认为true 如果关闭这里ZoneAwareNIWSDiscoveryLoadBalancer,或者只有单个zone的话,则走super.chooseServer(key) ZoneAwareLoadBalancer继承了DynamicServerListLoadBalancer,而DynamicServerListLoadBalancer继承了BaseLoadBalancer,而BaseLoadBalancer又继承了AbstractLoadBalancer。chooseServer方法在BaseLoadBalancer中有实现。

BaseLoadBalancer.chooseServer

ribbon-loadbalancer-2.2.5-sources.jar!/com/netflix/loadbalancer/BaseLoadBalancer.java

    public Server chooseServer(Object key) {if (counter == null) {counter = createCounter();}counter.increment();if (rule == null) {return null;} else {try {return rule.choose(key);} catch (Exception e) {logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);return null;}}}

这里可以看到最后是调用rule.choose(key),这个rule默认是ZoneAvoidanceRule

RibbonClientConfiguration

spring-cloud-netflix-ribbon-2.0.0.RC1-sources.jar!/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java

@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {//......@Bean@ConditionalOnMissingBeanpublic IRule ribbonRule(IClientConfig config) {if (this.propertiesFactory.isSet(IRule.class, name)) {return this.propertiesFactory.get(IRule.class, config, name);}ZoneAvoidanceRule rule = new ZoneAvoidanceRule();rule.initWithNiwsConfig(config);return rule;}
}

可以看到这里指定了ZoneAvoidanceRule

PredicateBasedRule.choose

ribbon-loadbalancer-2.2.5-sources.jar!/com/netflix/loadbalancer/PredicateBasedRule.java

/*** A rule which delegates the server filtering logic to an instance of {@link AbstractServerPredicate}.* After filtering, a server is returned from filtered list in a round robin fashion.* * * @author awang**/
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.*/@Overridepublic Server choose(Object key) {ILoadBalancer lb = getLoadBalancer();Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);if (server.isPresent()) {return server.get();} else {return null;}       }
}

可以看到这里是调用getPredicate().chooseRoundRobinAfterFiltering方法

AbstractServerPredicate.chooseRoundRobinAfterFiltering

ribbon-loadbalancer-2.2.5-sources.jar!/com/netflix/loadbalancer/AbstractServerPredicate.java

    /*** Choose a server in a round robin fashion after the predicate filters a given list of servers and load balancer key. */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())));}/*** Get servers filtered by this predicate from list of servers. */public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {if (loadBalancerKey == null) {return ImmutableList.copyOf(Iterables.filter(servers, this.getServerOnlyPredicate()));            } else {List<Server> results = Lists.newArrayList();for (Server server: servers) {if (this.apply(new PredicateKey(loadBalancerKey, server))) {results.add(server);}}return results;            }}

而chooseRoundRobinAfterFiltering调用的是getEligibleServers(servers, loadBalancerKey)方法 这里loadBalancerKey不为null,走的是else里头的apply方法。

CompositePredicate.getEligibleServers

ribbon-loadbalancer-2.2.5-sources.jar!/com/netflix/loadbalancer/CompositePredicate.java

    @Overridepublic boolean apply(@Nullable PredicateKey input) {return delegate.apply(input);}/*** Get the filtered servers from primary predicate, and if the number of the filtered servers* are not enough, trying the fallback predicates  */@Overridepublic List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {List<Server> result = super.getEligibleServers(servers, loadBalancerKey);Iterator<AbstractServerPredicate> i = fallbacks.iterator();while (!(result.size() >= minimalFilteredServers && result.size() > (int) (servers.size() * minimalFilteredPercentage))&& i.hasNext()) {AbstractServerPredicate predicate = i.next();result = predicate.getEligibleServers(servers, loadBalancerKey);}return result;}

CompositePredicate重写了getEligibleServers方法,可以看到先调用了父类AbstractServerPredicate的getEligibleServers,然后再对fallback等逻辑进行处理

ZoneAvoidanceRule

ribbon-loadbalancer-2.2.5-sources.jar!/com/netflix/loadbalancer/ZoneAvoidanceRule.java

/*** A rule that uses the a {@link CompositePredicate} to filter servers based on zone and availability. The primary predicate is composed of* a {@link ZoneAvoidancePredicate} and {@link AvailabilityPredicate}, with the fallbacks to {@link AvailabilityPredicate}* and an "always true" predicate returned from {@link AbstractServerPredicate#alwaysTrue()} * * @author awang**/
public class ZoneAvoidanceRule extends PredicateBasedRule {//......private CompositePredicate compositePredicate;public ZoneAvoidanceRule() {super();ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this);AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this);compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);}private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {return CompositePredicate.withPredicates(p1, p2).addFallbackPredicate(p2).addFallbackPredicate(AbstractServerPredicate.alwaysTrue()).build();}@Overridepublic AbstractServerPredicate getPredicate() {return compositePredicate;}
}

这里的getPredicate返回的是CompositePredicate,是组合Predicate,ZoneAvoidancePredicate及AvailabilityPredicate,如果二者and不满足,则fallback到AvailabilityPredicate,再不满足,则fallback到AbstractServerPredicate.alwaysTrue(),即返回true,不过滤。

小结

  • RibbonLoadBalancerClient.choose方法里头,调用loadBalancer.chooseServer("default"),写死了loadBalancerKey。
  • loadBalancer.chooseServer方法最后是委托给IRule的choose方法,默认是使用ZoneAvoidanceRule,其内部又委托给AbstractServerPredicate的chooseRoundRobinAfterFiltering方法进行过滤。
  • 最后调用的是CompositePredicate的apply进行筛选,如果ZoneAvoidancePredicate与AvailabilityPredicate的and不通过,则看AvailabilityPredicate是否通过,如果再不通过则返回true,不进行过滤。

doc

  • Client Side Load Balancer: Ribbon

转载于:https://my.oschina.net/go4it/blog/1820178

聊聊RibbonLoadBalancerClient的choose方法相关推荐

  1. 聊聊MultipartFile的transferTo方法

    默认已经知道:MultipartFile  这个类一般是用来接受前台传过来的文件. 那transferTo方法有什么作用呢? 打开源码: public void transferTo(File des ...

  2. 聊聊线程之run方法

    话不多说 参考书籍 汪文君 补充知识:start是异步,run是同步,start的执行会经过JNI方法然后被任务执行调度器告知给系统内核分配时间片进行创建线程并执行,而直接调用run不经过本地方法就是 ...

  3. SpringCloud(Hoxton SR6)微服务工具集学习笔记

    Spring Cloud 微服务工具集(版本: Hoxton SR6) Spring Cloud 微服务工具集 1.什么是微服务 2.为什么是微服务? 单体应用 微服务架构应用 架构的演变 3.微服务 ...

  4. 微服务常见面试题(Java、数据库、Redis、SpringCloud面试题)

    Redis 本质上是一个 Key-Value 类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据 flush 到硬 盘上进行保存. 因为是纯内 ...

  5. restTemplate loadbalance 负载均衡使用demo 案例 原理以及全网最细源码解析

    restTemplate 是spring 提供的http请求工具,类似于httpclient, 默认情况下与其他的http 工具类没有区别 但是当添加了@Loadbalance 注解之后,则具备了负载 ...

  6. springcloud之负载均衡

    文章目录 1.1 两种负载均衡 客户端负载均衡 服务端负载均衡 1.2 概念 1.3 Ribbon组成 1.4 编码及测试 1.4.1 利用Eureka手写负载均衡: 1.4.2 ribbon负载均衡 ...

  7. mybatis动态sql中的if、where、choose、trim、foreach方法

    mybatis笔记03 1.动态sql语句–>sql拼接 1.1 if方法 相当于el表达式中的if(){}语句 参数类型不同,if标签中test的参数是怎么说明的呢? 1)如果参数的类型是数字 ...

  8. Python numpy.choose函数方法的使用

    NumPy(Numerical Python的缩写)是一个开源的Python科学计算库.使用NumPy,就可以很自然地使用数组和矩阵.NumPy包含很多实用的数学函数,涵盖线性代数运算.傅里叶变换和随 ...

  9. oracle hash join outer,CSS_浅谈Oracle中的三种Join方法,基本概念 Nested loop join: Outer - phpStudy...

    浅谈Oracle中的三种Join方法 基本概念 Nested loop join: Outer table中的每一行与inner table中的相应记录join,类似一个嵌套的循环. Sort mer ...

最新文章

  1. PHP实现文件下载断点续传详解
  2. python北京理工大学推荐的书-Python教程书籍(北理工第2版)思考练习-第三章
  3. 如何让SQLServer的 itemNum 字段 按照数字大小顺序排序
  4. quartz获取开始结束时间_王者荣耀s21什么时候开始是9月24日吗?王者荣耀s20赛季结束时间...
  5. 工程图样中粗实线的用途_图纸天天画,粗实线和细实线的线宽比例是多少?2:1还是3:1?...
  6. 解决:vue项目的页面刷新之 title被重置问题
  7. 基于java ee的源码,基于java EE的云ERP系统
  8. 【Elasticsearch】数据预加载
  9. android手机常用功能,Windows Phone 7/Android手机常用功能对比
  10. 为什么要有handler机制
  11. C#使用PDF控件打开PDF
  12. Chrome浏览器无法访问网页(移动硬盘)
  13. Linux下把ncsi设置成OCP模式,NCSI简介
  14. 互动拍照 — 子弹时间
  15. 计算机专业博士后 解决北京户口,在职博士后落北京户口可以么?该如何操作
  16. U盘显示0字节怎么恢复完整解决教程
  17. 基于可见光通信的移动机器人室内定位及物联网应用
  18. SPL工业智能:发现时序数据的异常
  19. git pull常见操作
  20. java窗口如何定时关闭_如何使用GreaseMonkey关闭定时弹出的窗口?

热门文章

  1. 奇瑞a3中控按键图解_纷纷亮相,奇瑞众多黑科技悄然现身,焦点全在星途VX智能座舱上...
  2. 用jsp上传文件时报错,是tomcat的文件夹为只读。修改只读权限后正常
  3. document.body.offsetWidth 网页可见区域高宽,offset、client、scroll使用方法详解,页面位置距离
  4. js中delete是es6_js和es6中的字符串方法总结
  5. 计算机科学与技术的程序设计基础,程序设计基础
  6. java 文件md5校验_Java 获取 文件md5校验码
  7. Java设计模式(建造者模式-适配器模式-桥接模式)
  8. 测试一款CSDN免费下载软件
  9. 压力传感器变送器读取数据测试:MIK-BSQW
  10. 关于三岔路口双车接力,这位同学把问题总算问清楚了