在系统中可以启动多个 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. 中国地区免费注册bitcointalk论坛教程

    bitcointalk论坛是著名的老牌比特币论坛,中本聪当年也在这里和各路大神探讨.但现在国家的高墙禁止网民访问. 你可能会用一个国外的代理工具来看贴,看贴确实可以,但是如果想注册,注册完后就会发现帐 ...

  2. 【BZOJ】3926: [Zjoi2015]诸神眷顾的幻想乡

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3926 广义后缀自动机... 久仰公之大名啊... 太阳花田的结构比较特殊,只与一个空地相邻 ...

  3. class与struct的区别

    C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能: ①struct能包含成员函数吗? 能! ②struct能继承吗? 能!! ...

  4. Ubuntu18.04的网络配置

    网卡与DNS配置 1)打开命令窗口(右键单机桌面选择Open Terminal或者用快捷键Ctrl+Alt+T打开终端),输入ip a查看自己的网卡编号 2)输入命令sudo vim /etc/net ...

  5. Python 增强类库

    程序中断 # coding=utf-8 支持中文 re = iter(range(5)) try: for i in range(100): print re.next() except StopIt ...

  6. openmodelica警告及错误

    Warning: The initial conditions are not fully specified. simulate(TCS.TCS,startTime=0,stopTime=200.0 ...

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

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

  8. 学习笔记1—python基础

    1.安装pip: python -m pip install -U pip (打开命令行窗口:Anaconda Prompt) 升级:python -m pip install --upgrade p ...

  9. jfinal集成cas单点认证实践

    本示例jfinal集成cas单点认证,采用获取到登录用户session信息后,在本地站点备份一份session信息,主要做以下几个步骤: 1.站点引入响应jar包: 2.在web.xml中配置对应过滤 ...

  10. Lua面向对象之一:简单例子

    1.Lua面向对象实现步骤 ①创建一个全局表(称之为元表) ②设置这个元表的__index值(值通常为元表自己,这样就能通过__index查找到对应的属性和方法) __index 赋值其实是一个fun ...