在系统中可以启动多个 provider 实例,consumer 发起远程调用时,根据指定的负载均衡算法选择一个 provider。

在本机配置多个 provider,使用不同的端口:

<dubbo:protocol name="dubbo" port="20880"/>

<dubbo:protocol name="dubbo" port="20881"/>

<dubbo:protocol name="dubbo" port="20882"/>

consumer 配置 loadbalance:

<dubbo:reference id="hello" loadbalance="roundrobin" interface="com.zhang.HelloService" />

dubbo 2.1.2 提供了4种不同的负载均衡算法,在 /META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance 文件中:

adptive=com.alibaba.dubbo.rpc.cluster.loadbalance.LoadBalanceAdptive
random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
leastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
consistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance

分别对应随机、轮询、最少活跃、一致性哈希。
随机、轮训都是先考虑权重,如果没有设置权重或者每个 provider 的权重相同,则退化成完全的随机和轮训,最少活跃没看明白。

负载均衡的粒度是单个方法,例: com.zhang.HelloService.sayHello() 有一个负载均衡的 selector。

轮询思想就是:维持一个 map,“接口名 + 方法名”作为建,一个计数器作为值,每次调用接口时,增加计数器然后取模。

public class RoundRobinLoadBalance extends AbstractLoadBalance {
public static final String NAME = "roundrobin";
private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
private final ConcurrentMap<String, AtomicPositiveInteger> weightSequences = new ConcurrentHashMap<String, AtomicPositiveInteger>(); protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation) {
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
// 省略权重部分代码
AtomicPositiveInteger sequence = sequences.get(key);
if (sequence == null) {
sequences.putIfAbsent(key, new AtomicPositiveInteger());
sequence = sequences.get(key);
}
// 取模轮循
return invokers.get(sequence.getAndIncrement() % length);
} }

重点分析下一致性哈希吧,它的思想和jedis如出一辙。假定现在有invoker1,invoker2,invoker3,从invoker1衍生出160个符号,根据这些符号计算哈希值,然后把哈希值和 invoker 作为键值对放到 TreeMap 上。同样操作invoker2和invoker3。在选择invoker时,根据调用参数获取哈希值,然后从TreeMap上搜索对应的键值。

// com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance.ConsistentHashSelector
public ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
this.identityHashCode = System.identityHashCode(invokers);
URL url = invokers.get(0).getUrl(); // hash.nodes 默认为160,表示1个invoker对应160个符号,或者说160个符号指向这个invoker
this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);
// hash.arguments 默认为0,默认取调用方法的第1个参数值计算哈希值
String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0"));
argumentIndex = new int[index.length];
for (int i = 0; i < index.length; i ++) {
argumentIndex[i] = Integer.parseInt(index[i]);
}
for (Invoker<T> invoker : invokers) {
for (int i = 0; i < replicaNumber / 4; i++) {
byte[] digest = md5(invoker.getUrl().toFullString() + i);
for (int h = 0; h < 4; h++) {
long m = hash(digest, h);
// 把键值对挂到TreeMap上
virtualInvokers.put(m, invoker);
}
}
}
} public Invoker<T> select(Invocation invocation) {
//获取参数值
String key = toKey(invocation.getArguments());
//md5计算
byte[] digest = md5(key);
//计算哈希值,从TreeMap上取invoker
Invoker<T> invoker = sekectForKey(hash(digest, 0));
return invoker;
} private String toKey(Object[] args) {
StringBuilder buf = new StringBuilder();
for (int i : argumentIndex) {
if (i >= 0 && i < args.length) {
buf.append(args[i]);
}
}
return buf.toString();
} private Invoker<T> sekectForKey(long hash) {
Invoker<T> invoker;
Long key = hash;
if (!virtualInvokers.containsKey(key)) {
SortedMap<Long, Invoker<T>> tailMap = virtualInvokers.tailMap(key);
if (tailMap.isEmpty()) {
key = virtualInvokers.firstKey();
} else {
key = tailMap.firstKey();
}
}
invoker = virtualInvokers.get(key);
return invoker;
}

假定存在方法:

void com.zhang.HelloService.f1(int userid);
void com.zhang.HelloService.f2(int userid);
void com.zhang.HelloService.f3(int userid, Object param);

如果采用一致性哈希负载均衡,可以肯定的是,f1(10086) 的调用都会被转发到同一个的 provider,那 f1(10086) 和 f2(10086) 是否会转发到同一个 provider 呢?如果希望 f3(10086, param1) 和 f3(10086, param2) 都转发到相同的 provider,应该怎么做?

当然,我们也可以实现 AbstractLoadBalance 接口,使用自定义的负载均衡算法。

如果考虑到 provider 会下线,或者有新的 porvider 上线,则一致性哈希的 virtualInvokers 会重新计算。为什么要使用这种一致性哈希算法?

dubbo 负载均衡的更多相关文章

  1. Dubbo负载均衡与集群容错机制

    1  Dubbo简介 Dubbo是一款高性能.轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现. 作为一个轻量级RPC框架,D ...

  2. dubbo负载均衡策略和集群容错策略都有哪些

    dubbo负载均衡策略 random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重,会按照权 ...

  3. dubbo负载均衡策略和集群容错策略

    dubbo负载均衡策略 random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重,会按照权 ...

  4. 一文讲透Dubbo负载均衡之最小活跃数算法

    本文是对于Dubbo负载均衡策略之一的最小活跃数算法的详细分析.文中所示源码,没有特别标注的地方均为2.6.0版本. 为什么没有用截止目前的最新的版本号2.7.4.1呢?因为2.6.0这个版本里面有两 ...

  5. 3.dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?

    作者:中华石杉 面试题 dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢? 面试官心理分析 继续深问吧,这些都是用 dubbo 必须知道的一些东西,你得知道基本原理,知道序列化是什么协议 ...

  6. 分布式的几件小事(四)dubbo负载均衡策略和集群容错策略

    1.dubbo负载均衡策略 ①random loadbalance 策略 默认情况下,dubbo是random loadbalance 随机调用实现负载均衡,可以对provider不同实例设置不同的权 ...

  7. Dubbo入门到精通学习笔记(十一):Dubbo服务启动依赖检查、Dubbo负载均衡策略、Dubbo线程模型(结合Linux线程数限制配置的实战分享)

    文章目录 Dubbo服务启动依赖检查 Dubbo负载均衡策略 Dubbo线程模型(结合Linux线程数限制配置的实战分享) 实战经验分享( ** 属用性能调优**): Dubbo服务启动依赖检查 Du ...

  8. 面试系列24 dubbo负载均衡策略和集群容错策略

    (1)dubbo负载均衡策略 1)random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重 ...

  9. 面试系列16 dubbo负载均衡策略和集群容错策略都有哪些?动态代理策略呢

    (1)dubbo负载均衡策略 1)random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重 ...

  10. Dubbo 负载均衡的实现

    前言 负载均衡是指在集群中,将多个数据请求分散在不同单元上进行执行,主要为了提高系统容错能力和加强系统对数据的处理能力. 在 Dubbo 中,一次服务的调用就是对所有实体域 Invoker 的一次筛选 ...

随机推荐

  1. jenkins+ant+jmeter自动化性能测试平台

    jenkins+ant+jmeter自动化性能测试平台 Jmeter是性能测试的工具,java编写.开源,小巧方便,可以图形界面运行也可以在命令行下运行.网上已经有人使用ant来运行,http://w ...

  2. 基于 Python 和 Pandas 的数据分析(6) --- Joining and Merging

    这一节我们将看一下如何通过 join 和 merge 来合并 dataframe. import pandas as pd df1 = pd.DataFrame({'HPI':[80,85,88,85 ...

  3. _event_active

    EventId 事件ID GUID 对应creature或gameobject表中 guid,正数为生物,负数为物体 ActiveFlag 生物或物体激活时的flag,通常为0 NoticeText ...

  4. 转 Failed to run the WC DB work queue associated with 错误的解决

    svn 异常终止导致的缓存工作队列问题 解决方法:清空svn的队列 1.下载sqlite3.exe 2.找到你项目的.svn文件,查看是否存在wc.db 3.将sqlite3.exe放到.svn的同级 ...

  5. leetcode 29-> Divide Two Integers without using multiplication, division and mod operator

    class Solution(object): def divide(self, dividend, divisor): """ :type dividend: int ...

  6. python中enumerate内置库的使用

    使用enumerate,可以自动进行索引下标的赋值,本例代码中使用enumerate,进行excel单元格的赋值操作. 代码如果重复被调用,可将该代码封装成类进行使用 1 import openpyx ...

  7. [转]jsbsim基础概念

    转自: 么的聊链接:https://www.jianshu.com/p/a0b4598f928a 虽然用户不需要掌握太多 JSBSim 飞行模拟器的细节,但是了解 JSBSim 的基本工作流程也会对学 ...

  8. 分布式系统的唯一id生成算法你了解吗?

    在分库分表之后你必然要面对的一个问题,就是id咋生成? 因为要是一个表分成多个表之后,每个表的id都是从1开始累加自增长,那肯定不对啊. 举个例子,你的订单表拆分为了1024张订单表,每个表的id都从 ...

  9. 力扣(LeetCode)453. 最小移动次数使数组元素相等

    给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数.每次移动可以使 n - 1 个元素增加 1. 示例: 输入: [1,2,3] 输出: 3 解释: 只需要3次移动(注意每次移动 ...

  10. Spark之standalone模式

    standalone hdfs:namenode是主节点进程,datanode是从节点进程 yarn:resourcemanager是主节点进程,nodemanager是从节点进程 hdfs和yarn ...