一、Ribbon算法的介绍

Ribbon的源码地址:https://github.com/Netflix/ribbon

IRule:根据特定算法中从服务器列表中选取一个要访问的服务,Ribbon默认的算法为ZoneAvoidanceRule;

Ribbon中的7中负载均衡算法:

(1)RoundRobinRule:轮询;

(2)RandomRule:随机;

(3)AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;

(4)WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快的服务权重越大被选中的概率越大。刚启动时如果统计信息不足,则使用RoundRobinRule(轮询)策略,等统计信息足够,会切换到WeightedResponseTimeRule;

(5)RetryRule:先按照RoundRobinRule(轮询)策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务;

(6)BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;

(7)ZoneAvoidanceRule:复合判断Server所在区域的性能和Server的可用性选择服务器,在没有Zone的情况下是类似轮询的算法;

ribbion的负载均衡算法结构:

二、配置指定的负载均衡算法

1、打开消费者工程,增加如下的配置

@Configuration
public class ConfigBean
{
@Bean
@LoadBalanced //Ribbon 是客户端负载均衡的工具;
public RestTemplate getRestTemplate()
{
return new RestTemplate();
} //配置负载均衡的策略为随机,默认算法为轮询算法
@Bean
public IRule myRule()
{
//return new RoundRobinRule();
return new RandomRule();
}
}

2、启动类增加 @EnableEurekaClient 注解

@SpringBootApplication
@EnableEurekaClient //本服务启动后自动注册到eureka中(如果用了注册中心记得加)
public class DeptProvider8001_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptProvider8001_App.class, args);
}
}

3、然后重启这个消费者服务,访问;可以查看到随机访问生产者服务。

三、RetryRule(重试)

//具备重试机制的实例选择功能
public class RetryRule extends AbstractLoadBalancerRule {
//默认使用RoundRobinRule实例
IRule subRule = new RoundRobinRule();
//阈值为500ms
long maxRetryMillis = 500; public RetryRule() {
} public RetryRule(IRule subRule) {
this.subRule = (subRule != null) ? subRule : new RoundRobinRule();
} public RetryRule(IRule subRule, long maxRetryMillis) {
this.subRule = (subRule != null) ? subRule : new RoundRobinRule();
this.maxRetryMillis = (maxRetryMillis > 0) ? maxRetryMillis : 500;
} public void setRule(IRule subRule) {
this.subRule = (subRule != null) ? subRule : new RoundRobinRule();
} public IRule getRule() {
return subRule;
} public void setMaxRetryMillis(long maxRetryMillis) {
if (maxRetryMillis > 0) {
this.maxRetryMillis = maxRetryMillis;
} else {
this.maxRetryMillis = 500;
}
} public long getMaxRetryMillis() {
return maxRetryMillis;
} @Override
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
subRule.setLoadBalancer(lb);
} public Server choose(ILoadBalancer lb, Object key) {
long requestTime = System.currentTimeMillis();
long deadline = requestTime + maxRetryMillis; Server answer = null; answer = subRule.choose(key); if (((answer == null) || (!answer.isAlive()))
&& (System.currentTimeMillis() < deadline)) { InterruptTask task = new InterruptTask(deadline
- System.currentTimeMillis());
//反复重试
while (!Thread.interrupted()) {
//选择实例
answer = subRule.choose(key);
//500ms内没选择到就返回null
if (((answer == null) || (!answer.isAlive()))
&& (System.currentTimeMillis() < deadline)) {
/* pause and retry hoping it's transient */
Thread.yield();
}
else //若能选择到实例,就返回
{
break;
}
} task.cancel();
} if ((answer == null) || (!answer.isAlive())) {
return null;
} else {
return answer;
}
} @Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
} @Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}

四、WeightedResponseTimeRule(权重)

WeightedResponseTimeRule这个策略每30秒计算一次服务器响应时间,以响应时间作为权重,响应时间越短的服务器被选中的概率越大。它有一个LoadBalancerStats类,这里面有三个缓存,它的作用是记住发起请求服务提供者的一些参数,例如响应时间

有了缓存数据后权重是怎么处理的呢,下面看WeightedResponseTimeRule,它有一个定时任务,定时去计算权重

//该策略是对RoundRobinRule的扩展,增加了根据实例的运行情况来计算权重
//并根据权重来挑选实例,以达到更优的分配效果
public class WeightedResponseTimeRule extends RoundRobinRule { public static final IClientConfigKey<Integer> WEIGHT_TASK_TIMER_INTERVAL_CONFIG_KEY = new IClientConfigKey<Integer>() {
@Override
public String key() {
return "ServerWeightTaskTimerInterval";
} @Override
public String toString() {
return key();
} @Override
public Class<Integer> type() {
return Integer.class;
}
};
//默认30秒执行一次
public static final int DEFAULT_TIMER_INTERVAL = 30 * 1000; private int serverWeightTaskTimerInterval = DEFAULT_TIMER_INTERVAL; private static final Logger logger = LoggerFactory.getLogger(WeightedResponseTimeRule.class); // 存储权重的对象,该List中每个权重所处的位置对应了负载均衡器维护实例清单中所有实例在
//清单中的位置。
private volatile List<Double> accumulatedWeights = new ArrayList<Double>(); private final Random random = new Random(); protected Timer serverWeightTimer = null; protected AtomicBoolean serverWeightAssignmentInProgress = new AtomicBoolean(false); String name = "unknown"; public WeightedResponseTimeRule() {
super();
} public WeightedResponseTimeRule(ILoadBalancer lb) {
super(lb);
} @Override
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
if (lb instanceof BaseLoadBalancer) {
name = ((BaseLoadBalancer) lb).getName();
}
initialize(lb);
} void initialize(ILoadBalancer lb) {
if (serverWeightTimer != null) {
serverWeightTimer.cancel();
}
serverWeightTimer = new Timer("NFLoadBalancer-serverWeightTimer-"
+ name, true);
//启动一个定时任务,用来为每个服务实例计算权重,默认30秒执行一次,调用DynamicServerWeighTask方法,向下找
serverWeightTimer.schedule(new DynamicServerWeightTask(), 0,
serverWeightTaskTimerInterval);
// do a initial run
ServerWeight sw = new ServerWeight();
sw.maintainWeights(); Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
logger
.info("Stopping NFLoadBalancer-serverWeightTimer-"
+ name);
serverWeightTimer.cancel();
}
}));
} public void shutdown() {
if (serverWeightTimer != null) {
logger.info("Stopping NFLoadBalancer-serverWeightTimer-" + name);
serverWeightTimer.cancel();
}
} List<Double> getAccumulatedWeights() {
return Collections.unmodifiableList(accumulatedWeights);
} /*
第一步:生成一个[0,maxTotalWeight]的随机值
第二步:遍历权重列表,比较权重值与随机数的大小,如果权重值大于随机数,就拿当前权重列表
的索引值去服务实例表获取具体的实例。
*/
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
@Override
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; // 获取最后一个实例的权重
double maxTotalWeight = currentWeights.size() == 0 ? 0 : currentWeights.get(currentWeights.size() - 1);
// 如果最后一个实例的权重小于0.001,则采用父类实现的现象轮询的策略
if (maxTotalWeight < 0.001d || serverCount != currentWeights.size()) {
server = super.choose(getLoadBalancer(), key);
if(server == null) {
return server;
}
} else {
// 产生一个[0,maxTotalWeight]的随机值
double randomWeight = random.nextDouble() * maxTotalWeight;
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;
} class DynamicServerWeightTask extends TimerTask {
public void run() {
ServerWeight serverWeight = new ServerWeight();
try {
//点击maintainWeights可以进入权重计算的方法
serverWeight.maintainWeights();
} catch (Exception e) {
logger.error("Error running DynamicServerWeightTask for {}", name, e);
}
}
} class ServerWeight {
/*该函数主要分为两个步骤
1 根据LoadBalancerStats中记录的每个实例的统计信息,累计所有实例的平均响应时间,
得到总的平均响应时间totalResponseTime,该值用于后面的计算。
2 为负载均衡器中维护的实例清单逐个计算权重(从第一个开始),计算规则为:
weightSoFar+totalResponseTime-实例平均相应时间,其中weightSoFar初始化为0,并且
每计算好一个权重需要累加到weightSoFar上供下一次计算使用。
示例:4个实例A、B、C、D,它们的平均响应时间为10,40,80,100,所以总的响应时间为
230,每个实例的权重为总响应时间与实例自身的平均响应时间的差的累积所得,所以实例A
B,C,D的权重分别为:
A:230-10=220
B:220+230-40=410
C:410+230-80=560
D:560+230-100=690
需要注意的是,这里的权重值只是表示各实例权重区间的上限,并非某个实例的优先级,所以不
是数值越大被选中的概率就越大。而是由实例的权重区间来决定选中的概率和优先级。
A:[0,220]
B:(220,410]
C:(410,560]
D:(560,690)
实际上每个区间的宽度就是:总的平均响应时间-实例的平均响应时间,所以实例的平均响应时间越短
,权重区间的宽度越大,而权重区间宽度越大被选中的概率就越大。
*/
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) {
// no statistics, nothing to do
return;
}
double totalResponseTime = 0;
// 计算所有实例的平均响应时间的总和
for (Server server : nlb.getAllServers()) {
// 如果服务实例的状态快照不在缓存中,那么这里会进行自动加载
ServerStats ss = stats.getSingleServerStat(server);
totalResponseTime += ss.getResponseTimeAvg();
}
// 逐个计算每个实例的权重
Double weightSoFar = 0.0; // create new list and hot swap the reference
List<Double> finalWeights = new ArrayList<Double>();
//weightSoFar+totalResponseTime-实例平均相应时间
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);
} }
} void setWeights(List<Double> weights) {
this.accumulatedWeights = weights;
} @Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
super.initWithNiwsConfig(clientConfig);
serverWeightTaskTimerInterval = clientConfig.get(WEIGHT_TASK_TIMER_INTERVAL_CONFIG_KEY, DEFAULT_TIMER_INTERVAL);
} }

五、利用配置来重设负载均衡算法

根据文档要求进行配置权重算法

Debugger一下,可以看到进入了自己定义的权重里面来了,而且服务节点数什么都是对的,所以如果Ribbon提供的算法你觉得不够好,你就可以自己定义一个,其实和我之前自定义一个GhyPing一样,自己定义一个类然后继承抽象类 AbstractLoadBalancerRule,实现它的抽象方法

Ribbon提供的负载均衡算法IRule(四)的更多相关文章

  1. 负载均衡算法(四)IP Hash负载均衡算法

    /// <summary> /// IP Hash负载均衡算法 /// </summary> public static class IpHash { static Dicti ...

  2. 服务注册发现Eureka之三:Spring Cloud Ribbon实现客户端负载均衡(客户端负载均衡Ribbon之三:使用Ribbon实现客户端的均衡负载)

    在使用RestTemplate来消费spring boot的Restful服务示例中,我们提到,调用spring boot服务的时候,需要将服务的URL写死或者是写在配置文件中,但这两种方式,无论哪一 ...

  3. Ribbon核心组件IRule及配置指定的负载均衡算法

    Ribbon在工作时分为两步: 第一步:先选择 EurekaServer,它优先选择在同一个区域内负载较少的Server: 第二步:再根据用户指定的策略,在从Server取到的服务注册列表中选择一个地 ...

  4. Ribbon,主要提供客户侧的软件负载均衡算法。

    Ribbon Ribbon,主要提供客户侧的软件负载均衡算法.Ribbon客户端组件提供一系列完善的配置选项,比如连接超时.重试.重试算法等.Ribbon内置可插拔.可定制的负载均衡组件.下面是用到的 ...

  5. spring-cloud-starter-ribbon提供客户端的软件负载均衡算法

    Ribbon是什么? Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起.Ribbon客户端组件提供一系列完善的配置项如连接超时 ...

  6. SpringCloud全家桶学习之客户端负载均衡及自定义负载均衡算法----Ribbon(三)

    一.Ribbon是什么? Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端  负载均衡的工具(这里区别于nginx的负载均衡).简单来说,Ribbon是Netf ...

  7. Spring Cloud(十四):Ribbon实现客户端负载均衡及其实现原理介绍

    年后到现在一直很忙,都没什么时间记录东西了,其实之前工作中积累了很多知识点,一直都堆在备忘录里,只是因为近几个月经历了一些事情,没有太多的经历来写了,但是一些重要的东西,我还是希望能坚持记录下来.正好 ...

  8. Ribbon源码分析(一)-- RestTemplate 以及自定义负载均衡算法

    如果只是想看ribbon的自定义负载均衡配置,请查看: https://www.cnblogs.com/yangxiaohui227/p/13186004.html 注意: 1.RestTemplat ...

  9. springcloud第四步:ribbon搭建服务负载均衡

    使用ribbon实现负载均衡 启动两个会员服务工程,端口号分别为8762.8763,订单服务 使用负载均衡策略轮训到会员服务接口. 什么是ribbon ribbon是一个负载均衡客户端 类似nginx ...

随机推荐

  1. FL studio系列教程(十六):FL Studio查看菜单讲解

    FL Studio中每个窗口的显示.隐藏和布局命令都在查看菜单中.其中它被分为窗口.布局和浏览器3个部分,各项名称都有其单独的作用.窗口部分主要是软件的显示的一些菜单这里就不详细讲解了,接下来我们重点 ...

  2. python菜鸟教程学习9:函数

    函数的定义 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段.python提供了很多内建函数,但我们依然可以自己创建函数,叫做用户自定义函数. 自定义函数 你可以定义一个由自己想要功能 ...

  3. seata

    启动seataserver: 回滚日志: server日志: file模式的文件 整个过程如果观察数据库变化的话,会发现事务是先提交了的,出现异常之后由seata又回滚回去

  4. json套娃其实是这样套的!

  5. rsync单项同步

    配置rsync+inotify实时单向同步 定期同步的缺点: 执行备份的时间固定,延期明显,实时性差 当同步源长期不变化时,密集的定期任务是不必要的(浪费资源) 实时同步的优点: 一旦同步源出现变化, ...

  6. python—数据类型和变量

    在python中,能够直接处理的数据类型和变量有整数.浮点数.字符串.布尔值.空值.变量. 一.整数 1.python可处理任意大小的整数,包括负整数,在程序中的表示方法与在数学中的方法一样.例如:0 ...

  7. LeetCode 010 Regular Expression Matching

    题目描述:Regular Expression Matching Implement regular expression matching with support for '.' and '*' ...

  8. 09_消息通知Toast和Notification

    1. Toast 学习创建长短不一的Toast提示,并自定义Toast在屏幕上的位置以及Toast的外观. 1 package com.example.toastdemo; 2 3 import an ...

  9. 08_UI控件

    uiControl整体界面如下图所示,按照视频教程,学习控件由于是初学,都是最基础知识.还有ImageSwitcher.Gallery未更新,o(╯□╰)o 1 package com.example ...

  10. charles功能(五)屏蔽web网页的抓包信息(proxy)

    应用场景:屏蔽web网页的抓包信息 proxy-->windows proxy(前面没有对勾,就不会抓到 PC浏览器的包) proxy-->macOS proxy(mac电脑) 最终效果