前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[享学Netflix] 五十三、Ribbon的LoadBalancer五大组件之:IRule(三)随机和重试,所有IRule实现总结

[享学Netflix] 五十三、Ribbon的LoadBalancer五大组件之:IRule(三)随机和重试,所有IRule实现总结

作者头像
YourBatman
发布2020-03-20 11:18:23
1.2K0
发布2020-03-20 11:18:23
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦

任何你写的代码,超过6个月不去看它,当你再看时,都像是别人写的。

代码下载地址:https://github.com/f641385712/netflix-learning

前言

关于IRule的实现,还差两个实现规则,一个是随机规则RandomRule,一个是重试规则RetryRule,本文将进行收尾,并且给出对所有IRule实现的总结列表。


正文

RandomRule 随机规则

随机选择一个server。使用ThreadLocalRandom.current().nextInt(serverCount);随机来一个。

代码语言:javascript
复制
public class RandomRule extends AbstractLoadBalancerRule {

	public Server choose(ILoadBalancer lb, Object key) {
		...
		Server server = null;
		while (server == null) {
			...
			List<Server> upList = lb.getReachableServers();
			List<Server> allList = lb.getAllServers();
			int serverCount = allList.size();
            int index = chooseRandomInt(serverCount);
            server = upList.get(index);
            ...
		}
		return server;
	}

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

	@Override
	public Server choose(Object key) {
		return choose(getLoadBalancer(), key);
	}
}

说明:它的choose方法代码同RoundRobinRule的高度相似,但是RoundRobinRule计算随机值和get的时候使用的均是allServers,所以是没有bug的。但是本文是有bug的下,下面会使用示例模拟

这个算法非常的简单,但是,但是,但是,有个小细节你需要特别注意:

  • 随机数是通过allList.size(),也就是all所有Server的size
  • 但是,取值的时候却用upList.get(index)来取值

这很明显是个bug,下面我们通过代码示例来验证这个bug。


代码演示RandomRule的bug
代码语言:javascript
复制
@Test
public void fun2() throws InterruptedException {
    List<Server> serverList = new ArrayList<>();
    serverList.add(createServer("华南", 1));
    serverList.add(createServer("华东", 1));
    serverList.add(createServer("华东", 2));

    serverList.add(createServer("华北", 1));
    serverList.add(createServer("华北", 2));
    serverList.add(createServer("华北", 3));
    serverList.add(createServer("华北", 4));

    // 轮询策略:因为Servers均来自于lb,所以必须关联上一个lb实例哦
    ILoadBalancer lb = new BaseLoadBalancer();
    lb.addServers(serverList);
    RandomRule rule = new RandomRule();
    rule.setLoadBalancer(lb);
    System.out.println("server总数:"+lb.getAllServers().size());
    System.out.println("up的总数:"+lb.getReachableServers().size());

    while (true) {
        System.out.println(rule.choose(null));
        TimeUnit.SECONDS.sleep(2);
    }
}


private Server createServer(String zone, int index) {
    Server server = new Server("www.baidu" + zone + ".com", index);
    server.setAlive(true);
    server.setReadyToServe(true);
    server.setZone(zone);
    return server;
}

运行程序,控制台输出:

代码语言:javascript
复制
server总数:7
up的总数:7
www.baidu华东.com:1
www.baidu华北.com:1
www.baidu华北.com:1
www.baidu华北.com:2
www.baidu华北.com:1
www.baidu华东.com:1
www.baidu华南.com:1
...

完美,结果是完全随机的。这样不会出任何问题,因为本利all = up(都是7台)。下面模拟all > up的情况:

因为Server是否up是由IPing去决定的,因此只需要提供自定义的IPing规则便可模拟出现这个bug

代码语言:javascript
复制
lb.setPing(server -> server.getPort() % 10 > 2);

再次运行程序,抛错:

代码语言:javascript
复制
server总数:7
up的总数:2
java.lang.IndexOutOfBoundsException: Index: 1, Size: 0

	at java.util.ArrayList.rangeCheck(ArrayList.java:657)
	at java.util.ArrayList.get(ArrayList.java:433)
	at java.util.Collections$UnmodifiableList.get(Collections.java:1309)
	at com.netflix.loadbalancer.RandomRule.choose(RandomRule.java:61)
	at com.netflix.loadbalancer.RandomRule.choose(RandomRule.java:92)

因为upList只有2台,而总数是7台,所以如果随机出来的数字加入是3,那get(index)就抛错啦~


使用场景

几乎不用。更何况它还有bug呢,更不用喽。由于Ribbon工程目前处于维护状态,且该规则几乎不会使用,因此bug官方也不会修这个bug。万一你偏要使用,请你自行实现而不要使用这个有bug的版本。


RetryRule 重试规则

它可以在给定的任何IRule的基础上再包一层重试逻辑(默认给定的RoundRobinRule规则)。

代码语言:javascript
复制
public class RetryRule extends AbstractLoadBalancerRule {

	IRule subRule = new RoundRobinRule();
	// 最大重试时长  默认值是半分钟
	long maxRetryMillis = 500;
	
	... // 省略给这两个属性赋值的构造器和set方法们
	@Override
	public void setLoadBalancer(ILoadBalancer lb) {		
		super.setLoadBalancer(lb);
		subRule.setLoadBalancer(lb);
	}
	
	@Override
	public Server choose(Object key) {
		return choose(getLoadBalancer(), key);
	}

	// 选择方法
	public Server choose(ILoadBalancer lb, Object key) {
		// 计算出一个开始选择  和  结束选择的时间
		long requestTime = System.currentTimeMillis();
		long deadline = requestTime + maxRetryMillis;

		// 先通过subRule选择出一个server
		Server answer = null;
		answer = subRule.choose(key);
		
		// 如果选择出的Server为null,或者不是活的
		// 并且还在结束时间之前,就执行重试策略
		if (((answer == null) || (!answer.isAlive())) 
			&& (System.currentTimeMillis() < deadline)) {

			InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis());

			while (!Thread.interrupted()) {
				... // 持续不断的调用subRule.choose(key),知道获取到或者时间到了
			}
			task.cancel();
		}
		
		// 如果最终还是没获取到可用的,那就返回null。否则返回正常结果
		if ((answer == null) || (!answer.isAlive())) {
			return null;
		} else {
			return answer;
		}
	}
}

RetryRule中又定义了一个subRule,它默认的实现类是RoundRobinRule(你可以自己指定任何IRule实现),每次先采用subRule#choose()规则来选择一个服务实例,如果选到的实例正常就返回不需要重试;如果选择的服务实例为null或者已经失效,则在失效时间deadline之前不断的进行重试(重试时获取服务的策略还是subRule#choose()来选择),如果超过了deadline还是没取到则会返回一个null。


使用场景

因为它属于包装器模式的一种实现,因此在实际生产中,我个人推荐使用它来包装你实际的IRule,这样会使得实例Server更加的健康,对网络波动的容忍度更高些,如你可以这么做new RetryRule(new BestAvailableRule())

RetryRule配合上RoundRobinRule的组合(也就是默认组合)效果很好:因为RoundRobinRule失效的策略是超过10次,而如果在配合上RetryRule的话,容错性就会更强(当然相应的rt就会更长喽)~


IRule所有内置规则归纳

最后用一张表格来归纳所有的IRule实现:

规则名

父类

xxxxxxxxxxxxxxxxxxxxx

备注

RoundRobinRule

-

线性轮询

轮询index,选择index对应位置的server

WeightedResponseTimeRule

RoundRobinRule

根据rt分配一个权重值,rt时间越长,weight越小,被选中的可能性就越低

使用一个后台线程默认每30s重新计算一次权重值

BestAvailableRule

ClientConfigEnabled…

选择一个活跃请求数最小的Server

忽略已经被熔断的Server

PredicateBasedRule

ClientConfigEnabled…

基于断言器实现的规则

本类为抽象类,具体过滤规则交给子类

AvailabilityFilteringRule

PredicateBasedRule

过滤掉已熔断or活跃请求数太高的Server后,剩下的执行线性轮询

依赖于AvailabilityPredicate这个断言器实现过滤

ZoneAvoidanceRule

PredicateBasedRule

复合判断。先选出可用区,然后在按上规则筛选出复合条件的Server们,执行线性轮询

使用ZoneAvoidancePredicate和AvailabilityPredicate两个主断言器实现过滤

RandomRule

-

完全随机选择

此实现有bug,有bug,有bug

RetryRule

-

对任何IRule包一层重试机制

在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server

说明:若是直接extends AbstractLoadBalancerRule此处不写出,因为所有的实现均继承了它,没必要显示写出


总结

关于IRule的随机规则RandomRule和重试规则RetryRule就先介绍到这,本文首先了解了RandomRule随机规则他在生产环境几乎不会使用,并且重点是它还存在bug,不能使用。而重试规则RetryRule我个人推荐可以尝试在生产上使用,它使用起来也方便,能够提升系统的健康程度,当然牺牲的可能是rt时间~

到此LoadBalancer的五大核心组件就全部,非常详细的介绍完了,下章将进入负载均衡的调度中心ILoadBalancer的深入学习。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • RandomRule 随机规则
      • 代码演示RandomRule的bug
      • 使用场景
    • RetryRule 重试规则
      • 使用场景
    • IRule所有内置规则归纳
    • 总结
    相关产品与服务
    负载均衡
    负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档