ribbon源码(4) 载均衡算法
负载均衡算法模块主要的功能是从负载均衡器中获取服务器列表信息,根据算法选取出一个服务器。
IRule
负载均衡算法接口
- public interface IRule{
- public Server choose(Object key);//选择一个服务器
- public void setLoadBalancer(ILoadBalancer lb);//设置负载均衡器
- public ILoadBalancer getLoadBalancer(); //获取负载均衡器
- }
通过BaseLoadBalancer的setRule或构造函数来为BaseLoadBalancer添加IRule
- public void setRule(IRule rule) {
- if (rule != null) {
- this.rule = rule;
- } else {
- /* default rule */
- this.rule = new RoundRobinRule();
- }
- if (this.rule.getLoadBalancer() != this) {
- this.rule.setLoadBalancer(this);
- }
- }
RandomRule
生成一个随机数,从负载均衡器中选取一个服务器。
- public Server choose(ILoadBalancer lb, Object key) {
- if (lb == null) {
- return null;
- }
- Server server = null;
- while (server == null) {
- if (Thread.interrupted()) {
- return null;
- }
- List<Server> upList = lb.getReachableServers();
- List<Server> allList = lb.getAllServers();
- int serverCount = allList.size();
- if (serverCount == 0) {
- return null;
- }
- int index = rand.nextInt(serverCount);
- server = upList.get(index);
- if (server == null) {
- Thread.yield();
- continue;
- }
- if (server.isAlive()) {
- return (server);
- }
- server = null;
- Thread.yield();
- }
- return server;
- }
RoundRobinRule
轮询从负载均衡器中选取一个服务器。
- public Server choose(ILoadBalancer lb, Object key) {
- if (lb == null) {
- log.warn("no load balancer");
- return null;
- }
- Server server = null;
- int count = 0;
- 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);
- if (server == null) {
- /* Transient. */
- Thread.yield();
- continue;
- }
- if (server.isAlive() && (server.isReadyToServe())) {
- return (server);
- }
- server = null;
- }
- if (count >= 10) {
- log.warn("No available alive servers after 10 tries from load balancer: "
- + lb);
- }
- return server;
- }
BestAvailableRule
选择并发量最小且没有被熔断的服务器,需要使用到LoadBalancerStats来获取服务器的状态。
- public Server choose(Object key) {
- if (loadBalancerStats == null) {
- return super.choose(key);
- }
- List<Server> serverList = getLoadBalancer().getAllServers();
- int minimalConcurrentConnections = Integer.MAX_VALUE;
- long currentTime = System.currentTimeMillis();
- Server chosen = null;
- for (Server server: serverList) {
- ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);
- if (!serverStats.isCircuitBreakerTripped(currentTime)) {
- int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
- if (concurrentConnections < minimalConcurrentConnections) {
- minimalConcurrentConnections = concurrentConnections;
- chosen = server;
- }
- }
- }
- if (chosen == null) {
- return super.choose(key);
- } else {
- return chosen;
- }
- }
WeightedResponseTimeRule
按照响应时间的比例来选择服务器。首先内部会有一个定时器,定时从负载均衡器里面读取服务器的平均响应时间,然后根据平均响应时间转换成权重。
- class DynamicServerWeightTask extends TimerTask {
- public void run() {
- ServerWeight serverWeight = new ServerWeight();
- try {
- serverWeight.maintainWeights();
- } catch (Exception e) {
- logger.error("Error running DynamicServerWeightTask for {}", name, e);
- }
- }
- }
- class ServerWeight {
- public void maintainWeights() {
- ILoadBalancer lb = getLoadBalancer();
- if (lb == null) {
- return;
- }
- if (!serverWeightAssignmentInProgress.compareAndSet(false, true)) {
- return;
- }
- try {
- logger.info("Weight adjusting job started");
- AbstractLoadBalancer nlb = (AbstractLoadBalancer) lb;
- LoadBalancerStats stats = nlb.getLoadBalancerStats();
- if (stats == null) {
- return;
- }
- double totalResponseTime = 0;
- // find maximal 95% response time
- for (Server server : nlb.getAllServers()) {
- // this will automatically load the stats if not in cache
- ServerStats ss = stats.getSingleServerStat(server);
- totalResponseTime += ss.getResponseTimeAvg();
- }
- // weight for each server is (sum of responseTime of all servers - responseTime)
- // so that the longer the response time, the less the weight and the less likely to be chosen
- Double weightSoFar = 0.0;
- // create new list and hot swap the reference
- List<Double> finalWeights = new ArrayList<Double>();
- for (Server server : nlb.getAllServers()) {
- ServerStats ss = stats.getSingleServerStat(server);
- double weight = totalResponseTime - ss.getResponseTimeAvg();
- weightSoFar += weight;
- finalWeights.add(weightSoFar);
- }
- setWeights(finalWeights);
- } catch (Exception e) {
- logger.error("Error calculating server weights", e);
- } finally {
- serverWeightAssignmentInProgress.set(false);
- }
- }
- }
然后根据权重来选择服务器
- public Server choose(ILoadBalancer lb, Object key) {
- if (lb == null) {
- return null;
- }
- Server server = null;
- while (server == null) {
- // get hold of the current reference in case it is changed from the other thread
- List<Double> currentWeights = accumulatedWeights;
- if (Thread.interrupted()) {
- return null;
- }
- List<Server> allList = lb.getAllServers();
- int serverCount = allList.size();
- if (serverCount == 0) {
- return null;
- }
- int serverIndex = 0;
- // last one in the list is the sum of all weights
- double maxTotalWeight = currentWeights.size() == 0 ? 0 : currentWeights.get(currentWeights.size() - 1);
- // No server has been hit yet and total weight is not initialized
- // fallback to use round robin
- if (maxTotalWeight < 0.001d) {
- server = super.choose(getLoadBalancer(), key);
- if(server == null) {
- return server;
- }
- } else {
- // generate a random weight between 0 (inclusive) to maxTotalWeight (exclusive)
- double randomWeight = random.nextDouble() * maxTotalWeight;
- // pick the server index based on the randomIndex
- int n = 0;
- for (Double d : currentWeights) {
- if (d >= randomWeight) {
- serverIndex = n;
- break;
- } else {
- n++;
- }
- }
- server = allList.get(serverIndex);
- }
- if (server == null) {
- /* Transient. */
- Thread.yield();
- continue;
- }
- if (server.isAlive()) {
- return (server);
- }
- // Next.
- server = null;
- }
- return server;
- }
AvailabilityFilteringRule
使用RoundRobinRule来选择服务器,并且通过AvailabilityPredicate进行筛选。AvailabilityPredicate会剔除熔断的和超过指定并发量的server。
- public Server choose(Object key) {
- int count = 0;
- Server server = roundRobinRule.choose(key);
- while (count++ <= 10) {
- if (predicate.apply(new PredicateKey(server))) {
- return server;
- }
- server = roundRobinRule.choose(key);
- }
- return super.choose(key);
- }
AvailabilityPredicate:
- public boolean apply(@Nullable PredicateKey input) {
- LoadBalancerStats stats = getLBStats();
- if (stats == null) {
- return true;
- }
- return !shouldSkipServer(stats.getSingleServerStat(input.getServer()));
- }
- private boolean shouldSkipServer(ServerStats stats) {
- if ((CIRCUIT_BREAKER_FILTERING.get() && stats.isCircuitBreakerTripped())
- || stats.getActiveRequestsCount() >= activeConnectionsLimit.get()) {
- return true;
- }
- return false;
- }
使用AvailabilityFilteringRule涉及配置:
属性 | 实现 | 默认值 |
niws.loadbalancer.availabilityFilteringRule.filterCircuitTripped | 是否剔除熔断server | true |
niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit |
最大连接数 | Integer.MAX_VALUE |
ZoneAvoidanceRule
这个rule虽然继承了PredicateBasedRule但是在使用上都与上面的rule不一样,其实他的核心主要是为ZoneAwareLoadBalancer提供了筛选zone的静态方法,他并不通用。
静态方法getAvailableZones,会遍历所有的zone,以zone为单位,检查各个zone的实例个数,熔断比率,来决定是否包含改zone。
静态方法createSnapshot,将LoadBalancerStats按zone返回map结构
类图
Predicate
用于过滤服务器,ribbon提供了三个过滤条件,AvailabilityPredicate、ZoneAvoidancePredicate、ZoneAffinityPredicate。PredicateKey为过滤的参数。
ribbon源码(4) 载均衡算法的更多相关文章
- Spring Cloud Ribbon 源码分析---负载均衡算法
上一篇分析了Ribbon如何发送出去一个自带负载均衡效果的HTTP请求,本节就重点分析各个算法都是如何实现. 负载均衡整体是从IRule进去的: public interface IRule{ /* ...
- Spring Cloud Ribbon源码分析---负载均衡实现
上一篇结合 Eureka 和 Ribbon 搭建了服务注册中心,利用Ribbon实现了可配置负载均衡的服务调用.这一篇我们来分析Ribbon实现负载均衡的过程. 从 @LoadBalanced入手 还 ...
- ribbon源码(2) 负载均衡器
负载均衡器对外提供负载均衡的功能,本质上是是维护当前服务的服务器列表和服务器状态,通过负载均衡算法选取合适的服务器地址. 用户可以通过实现ILoadBalancer来实现自己的负载均衡器,ribbon ...
- Ribbon源码分析(二)-- 服务列表的获取和负载均衡算法分析
上一篇博客(https://www.cnblogs.com/yangxiaohui227/p/12614343.html)分享了ribbon如何实现对http://product/info/这个链接重 ...
- 【一起学源码-微服务】Ribbon 源码四:进一步探究Ribbon的IRule和IPing
前言 前情回顾 上一讲深入的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList,同时在Dynam ...
- 【一起学源码-微服务】Ribbon源码五:Ribbon源码解读汇总篇~
前言 想说的话 [一起学源码-微服务-Ribbon]专栏到这里就已经全部结束了,共更新四篇文章. Ribbon比较小巧,这里是直接 读的spring cloud 内嵌封装的版本,里面的各种config ...
- ribbon源码(1) 概述
ribbon的核心功能是提供客户端在进行网络请求时负载均衡的能力.主要有以下几个模块: 负载均衡器模块 负载均衡器模块提供了负载均衡能力,详细参见ribbon源码之负载均衡器. 配置模块 配置模块管理 ...
- Android开源项目 Universal imageloader 源码研究之Lru算法
https://github.com/nostra13/Android-Universal-Image-Loader universal imageloader 源码研究之Lru算法 LRU - Le ...
- Spring源码加载过程图解(一)
最近看了一下Spring源码加载的简装版本,为了更好的理解,所以在绘图的基础上,进行了一些总结.(图画是为了理解和便于记忆Spring架构) Spring的核心是IOC(控制反转)和AOP(面向切面编 ...
随机推荐
- Kubernetes 的层级命名空间介绍
原文链接:https://fuckcloudnative.io/posts/introducing-hierarchical-namespaces/ 在单个 Kubernetes 集群上安全托管大量用 ...
- 实现0.5px边框线
实现0.5px边框方法 方案一:利用渐变(原理:高度1px,背景渐变,一半有颜色,一半透明) CSS部分 .container { width: 500px; margin: 0px auto; } ...
- Collections.synchronizedMap()与ConcurrentHashMap区别
Collections.synchronizedMap()与ConcurrentHashMap主要区别是:Collections.synchronizedMap()和Hashtable一样,实现上在调 ...
- Wireshark中的Checksum: 0x90c5 [validation disabled]问题
Wireshark中的Checksum: 0x90c5 [validation disabled]问题 废话不多说先上问题图: 这是我在做关于DNS协议PPT的时候出现的协议树第五项展开结果,可以发现 ...
- VyOS软路由系统基本设置
1. VyOS简介 VyOS是一个开源的网络操作系统,可以安装在物理硬件上,也可以安装在你自己的虚拟机上,或者是一个云平台上.它基于GNU/Linux,并加入了多个应用程序,如:Quagga, ISC ...
- 团队作业4:第三篇Scrum冲刺博客(歪瑞古德小队)
目录 一.Daily Scrum Meeting 1.1 会议照片 1.2 项目进展 二.项目燃尽图 三.签入记录 3.1 代码/文档签入记录 3.2 Code Review 记录 3.3 issue ...
- Java多线程_同步工具CyclicBarrier
CyclicBarrier概念:CyclicBarrier是多线程中的一个同步工具,它允许一组线程互相等待,直到到达某个公共屏障点.形象点儿说,CyclicBarrier就是一个屏障,要求这一组线程中 ...
- UnitTest单元测试框架解析【实用篇】
UnitTest是展开自动化测试的基础——这个框架很重要!首先我们先自己写一个测试类: 1.被测试类 Widthget.py: # coding: utf-8class Widthget: def _ ...
- python os库的使用方法 + 自动化安装第三方库脚本
一.os库基本介绍 os库提供通用的.基本的操作系统交互功能,包括windows.Mac os.linux os库是python标准库,包含几百个函数 常用路径操作.进程管理.环境参数等几类 路径操作 ...
- CefSharp如何判断页面是否加载完
问题:CefSharp如何判断页面是否加载完毕. 摘要:相信C#用CefSharp做浏览器来发的应该有很多人都会有遇到这个问题.特别是要执行JavaScript的时候,涉及到跨页面的JavaScrip ...