负载均衡算法模块主要的功能是从负载均衡器中获取服务器列表信息,根据算法选取出一个服务器。

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) 载均衡算法的更多相关文章

  1. Spring Cloud Ribbon 源码分析---负载均衡算法

    上一篇分析了Ribbon如何发送出去一个自带负载均衡效果的HTTP请求,本节就重点分析各个算法都是如何实现. 负载均衡整体是从IRule进去的: public interface IRule{ /* ...

  2. Spring Cloud Ribbon源码分析---负载均衡实现

    上一篇结合 Eureka 和 Ribbon 搭建了服务注册中心,利用Ribbon实现了可配置负载均衡的服务调用.这一篇我们来分析Ribbon实现负载均衡的过程. 从 @LoadBalanced入手 还 ...

  3. ribbon源码(2) 负载均衡器

    负载均衡器对外提供负载均衡的功能,本质上是是维护当前服务的服务器列表和服务器状态,通过负载均衡算法选取合适的服务器地址. 用户可以通过实现ILoadBalancer来实现自己的负载均衡器,ribbon ...

  4. Ribbon源码分析(二)-- 服务列表的获取和负载均衡算法分析

    上一篇博客(https://www.cnblogs.com/yangxiaohui227/p/12614343.html)分享了ribbon如何实现对http://product/info/这个链接重 ...

  5. 【一起学源码-微服务】Ribbon 源码四:进一步探究Ribbon的IRule和IPing

    前言 前情回顾 上一讲深入的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList,同时在Dynam ...

  6. 【一起学源码-微服务】Ribbon源码五:Ribbon源码解读汇总篇~

    前言 想说的话 [一起学源码-微服务-Ribbon]专栏到这里就已经全部结束了,共更新四篇文章. Ribbon比较小巧,这里是直接 读的spring cloud 内嵌封装的版本,里面的各种config ...

  7. ribbon源码(1) 概述

    ribbon的核心功能是提供客户端在进行网络请求时负载均衡的能力.主要有以下几个模块: 负载均衡器模块 负载均衡器模块提供了负载均衡能力,详细参见ribbon源码之负载均衡器. 配置模块 配置模块管理 ...

  8. Android开源项目 Universal imageloader 源码研究之Lru算法

    https://github.com/nostra13/Android-Universal-Image-Loader universal imageloader 源码研究之Lru算法 LRU - Le ...

  9. Spring源码加载过程图解(一)

    最近看了一下Spring源码加载的简装版本,为了更好的理解,所以在绘图的基础上,进行了一些总结.(图画是为了理解和便于记忆Spring架构) Spring的核心是IOC(控制反转)和AOP(面向切面编 ...

随机推荐

  1. Android 使用AS编译出错:找不到xx/desugar/debug/66.jar (系统找不到指定的文件。)

    以为是合作人配置文件的问题,后发现是缓存的问题,只需要Clean project,即可. 若提示无法删除目录:Unable to delete directory,或许是因为你打开了另一个项目,只需关 ...

  2. unity探索者之socket传输protobuf字节流(二)

    版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/6977935.html 上一篇主要说的是protobuf字节流的序列化和解析,将pr ...

  3. jq js 获取子元素

    js this.children[1].className=""this.firstChild.className = ""this.lastChild.cla ...

  4. Wireshark抓包与常见问题解决

    简介 Wireshark是一个网络抓包分析软件,当线上出现各种连接相关的问题,如连接不复用,大量CLOSE_WAIT时,可以方便的使用Wireshark抓包软件进行抓包分析 安装 Wirewark在w ...

  5. scp 转

    linux之cp/scp命令   名称:cp 使用权限:所有使用者 使用方式: cp [options] source dest cp [options] source... directory 说明 ...

  6. 在JAVASCRIPT中,为什么document.getElementById不可以再全局(函数外)使用?

    今天在使用JavaScript使用document.ElementById("ID")的时候,发现var x = document.getElementById("chi ...

  7. laravel核心Ioc容器

    laravel容器和依赖注入 啥是Ioc容器,方便我们实现依赖注入的一种实现,也就是说依赖注入不一定需要控制反转容器,只不过使用容器可能会方便些. laravel通过向容器中绑定接口的具体实现,可实现 ...

  8. 路由策略Routing Policy和策略路由PBR的区别

    这是面试的时候问道的一个问题,这里跟大家分享一下 路由策略(Routing Policy)是为了改变网络流量所经过的途径而修改路由信息的技术: PBR(policy-based-route)是一种依据 ...

  9. steam 数据转换

    目录 数组和集合互转 数组转集合 方法一 遍历 方法二 asList 方法三 steam 集合转数组 方法一 循环 方法二 toArray 方法三 steam 小结 string转为Character ...

  10. 深入了解Kafka【三】数据可靠性分析

    1.多副本数据同步策略 为了保障Prosucer发送的消息能可靠的发送到指定的Topic,Topic的每个Partition收到消息后,要向Producer发送ACK,如果Produser收到ACK, ...