源码走读:Dubbo带权重的随机负载均衡算法与warmup

2023-11-29 18:48

本文主要是介绍源码走读:Dubbo带权重的随机负载均衡算法与warmup,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在分布式架构中,当下游服务端刚启动时可能并不能承载上游瞬间大流量过来,通过warmup的机制,客户端可以根据下游服务端启动时间进行缓慢预热配比放量。而dubbo就通过注册启动时间戳的方式告知调用方自己的启动时间,客户端据此进行预热配比放量,避免对服务端造成重启!

这篇文章是承接上一篇文章:Dubbo如何实现基于http的jsonrpc调用。上篇中介绍了关于Dubbo中如何对jsonrpc进行http调用。最后我们提到了Dubbo默认的集群容错模式是failover。这里看下 边这个图中即将执行org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke方法。
在这里插入图片描述
进入 FailoverClusterInvoker#doInvoke方法中会执行到下边这行,用于选择目标节点的invoker对象。我们这一篇文章就重点分析下这个invoker是怎么来的?

//FailoverClusterInvoker.javaInvoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);

我们只选取关键代码:

//FailoverClusterInvoker.javaprotected Invoker<T> select(LoadBalance loadbalance, Invocation invocation,List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {.....Invoker<T> invoker = doSelect(loadbalance, invocation, invokers, selected);...return invoker;
}

而在doSlelect方法中又调用了loadbalance#select方法:

//AbstractClusterInvoker#doSelectprivate Invoker<T> doSelect(LoadBalance loadbalance, Invocation invocation,List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {......Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);....return invoker;
}

由dubbo-cluster中的AbstractLoadBalance.java类提供了这个select方法:

//AbstractLoadBalance#select@Overridepublic <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {if (CollectionUtils.isEmpty(invokers)) {return null;}if (invokers.size() == 1) {return invokers.get(0);}return doSelect(invokers, url, invocation);}protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);

可以看到里边有一个模板方法doSelect,即这里使用了模板模式。具体实现由子类提供,那么AbstractLoadBalance的子类有哪些呢?有下边5种:

org.apache.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance
org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
org.apache.dubbo.rpc.cluster.loadbalance.ShortestResponseLoadBalance

这里我们找一个最简单的看一下比如这里的随机负载均衡算法:

//RandomLoadBalance#doSelect/*** Select one invoker between a list using a random criteria* @param invokers List of possible invokers* @param url URL* @param invocation Invocation* @param <T>* @return The selected invoker*/@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// Number of invokersint length = invokers.size();// Every invoker has the same weight?boolean sameWeight = true;// the maxWeight of every invokers, the minWeight = 0 or the maxWeight of the last invokerint[] weights = new int[length];// The sum of weightsint totalWeight = 0;for (int i = 0; i < length; i++) {//获取下游服务权重int weight = getWeight(invokers.get(i), invocation);// SumtotalWeight += weight;// save for later useweights[i] = totalWeight;if (sameWeight && totalWeight != weight * (i + 1)) {sameWeight = false;}}if (totalWeight > 0 && !sameWeight) {// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.int offset = ThreadLocalRandom.current().nextInt(totalWeight);// Return a invoker based on the random value.for (int i = 0; i < length; i++) {if (offset < weights[i]) {return invokers.get(i);}}}// If all invokers have the same weight value or totalWeight=0, return evenly.return invokers.get(ThreadLocalRandom.current().nextInt(length));}

这是带权重的随机算法,使用int数组weights来存储不同下游节点的权重值,长度自然就是下游节点的个数。最后在从invokers列表选择invoker的时候,通过计算总权重的随机offset位移值来获取invoker。

 	int offset = ThreadLocalRandom.current().nextInt(totalWeight);

看注释可以知道,这是为了防止invokers列表中所有invoker的权重weight值之和小于等于0的情况,这种情况下没必要进行权重随机了,直接根据invokers列表长度随机就行了。

 		// If all invokers have the same weight value or totalWeight=0, return evenly.return invokers.get(ThreadLocalRandom.current().nextInt(length));

总体上,这个RandomLoadBalance#doSelect方法逻辑很简单。但其中循环获取每个invoker的权重这个方法需要引起重视。因为这个方法在下游节点配置了启动时间戳TIMESTAMP_KEY的情况下会进行warmup配比放量。也就是当下游服务端刚启动时可能并不能承载上游瞬间大流量过来,通过warmup的机制,客户端可以根据下游服务端启动时间进行缓慢预热配比放量。

	//AbstractLoadBalance.java提供int weight = getWeight(invokers.get(i), invocation);
	String TIMESTAMP_KEY = "timestamp";
//AbstractLoadBalance.java提供
/*** 获取考虑预热时间的情况下的调用权重值,如果下游服务的uptime启动时间在warmup预热时间内,那么下游服务的权重将会被减少!** @param invoker    the invoker* @param invocation the invocation of this invoker* @return weight*/int getWeight(Invoker<?> invoker, Invocation invocation) {int weight;URL url = invoker.getUrl();// Multiple registry scenario, load balance among multiple registries.if (REGISTRY_SERVICE_REFERENCE_PATH.equals(url.getServiceInterface())) {weight = url.getParameter(REGISTRY_KEY + "." + WEIGHT_KEY, DEFAULT_WEIGHT);} else {weight = url.getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);if (weight > 0) {long timestamp = invoker.getUrl().getParameter(TIMESTAMP_KEY, 0L);if (timestamp > 0L) {long uptime = System.currentTimeMillis() - timestamp;if (uptime < 0) {return 1;}//下游服务默认权重值100int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);//下游启动时间大于0且小于预热时间阈值则减少下游服务权重if (uptime > 0 && uptime < warmup) {weight = calculateWarmupWeight((int)uptime, warmup, weight);}}}}return Math.max(weight, 0);}/*** Calculate the weight according to the uptime proportion of warmup time* the new weight will be within 1(inclusive) to weight(inclusive)** @param uptime the uptime in milliseconds* @param warmup the warmup time in milliseconds* @param weight the weight of an invoker* @return weight which takes warmup into account*/static int calculateWarmupWeight(int uptime, int warmup, int weight) {int ww = (int) ( uptime / ((float) warmup / weight));return ww < 1 ? 1 : (Math.min(ww, weight));}

到此为止,我们分析了从FailoverClusterInvoker#doInvoke一路下来到RandomLoadBalance#doSelect方法。有什么疑问多多交流!

这篇关于源码走读:Dubbo带权重的随机负载均衡算法与warmup的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/433923

相关文章

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

SpringCloud之LoadBalancer负载均衡服务调用过程

《SpringCloud之LoadBalancer负载均衡服务调用过程》:本文主要介绍SpringCloud之LoadBalancer负载均衡服务调用过程,具有很好的参考价值,希望对大家有所帮助,... 目录前言一、LoadBalancer是什么?二、使用步骤1、启动consul2、客户端加入依赖3、以服务

SpringCloud负载均衡spring-cloud-starter-loadbalancer解读

《SpringCloud负载均衡spring-cloud-starter-loadbalancer解读》:本文主要介绍SpringCloud负载均衡spring-cloud-starter-loa... 目录简述主要特点使用负载均衡算法1. 轮询负载均衡策略(Round Robin)2. 随机负载均衡策略(

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1