dubbo 负载均衡
在系统中可以启动多个 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 负载均衡的更多相关文章
- Dubbo负载均衡与集群容错机制
1 Dubbo简介 Dubbo是一款高性能.轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现. 作为一个轻量级RPC框架,D ...
- dubbo负载均衡策略和集群容错策略都有哪些
dubbo负载均衡策略 random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重,会按照权 ...
- dubbo负载均衡策略和集群容错策略
dubbo负载均衡策略 random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重,会按照权 ...
- 一文讲透Dubbo负载均衡之最小活跃数算法
本文是对于Dubbo负载均衡策略之一的最小活跃数算法的详细分析.文中所示源码,没有特别标注的地方均为2.6.0版本. 为什么没有用截止目前的最新的版本号2.7.4.1呢?因为2.6.0这个版本里面有两 ...
- 3.dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?
作者:中华石杉 面试题 dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢? 面试官心理分析 继续深问吧,这些都是用 dubbo 必须知道的一些东西,你得知道基本原理,知道序列化是什么协议 ...
- 分布式的几件小事(四)dubbo负载均衡策略和集群容错策略
1.dubbo负载均衡策略 ①random loadbalance 策略 默认情况下,dubbo是random loadbalance 随机调用实现负载均衡,可以对provider不同实例设置不同的权 ...
- Dubbo入门到精通学习笔记(十一):Dubbo服务启动依赖检查、Dubbo负载均衡策略、Dubbo线程模型(结合Linux线程数限制配置的实战分享)
文章目录 Dubbo服务启动依赖检查 Dubbo负载均衡策略 Dubbo线程模型(结合Linux线程数限制配置的实战分享) 实战经验分享( ** 属用性能调优**): Dubbo服务启动依赖检查 Du ...
- 面试系列24 dubbo负载均衡策略和集群容错策略
(1)dubbo负载均衡策略 1)random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重 ...
- 面试系列16 dubbo负载均衡策略和集群容错策略都有哪些?动态代理策略呢
(1)dubbo负载均衡策略 1)random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重 ...
- Dubbo 负载均衡的实现
前言 负载均衡是指在集群中,将多个数据请求分散在不同单元上进行执行,主要为了提高系统容错能力和加强系统对数据的处理能力. 在 Dubbo 中,一次服务的调用就是对所有实体域 Invoker 的一次筛选 ...
随机推荐
- Thymeleaf 模板引擎技术
引入Thymeleaf: <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf. ...
- nodejs的dependency.md
dependency和devDependency的区别 package-a --- package-b (dependency) --- | --- package-c (devDependency) ...
- JSONObject的parseArray方法作用。
该方法将字符串数据转换成集合对象. String dep_tree = JedisUtils.getInstance().get(CacheConstant.DEP_TREE, user.getId( ...
- codeforces 767C - Garland
题目链接:http://codeforces.com/contest/767/problem/C 问能否将一棵带点权的书分成点权$3$块,求任意方案. 其实考虑一棵以$x$为根的子树权值为${\fra ...
- [osg]osg绘制动态改变顶点的几何体
最简单的顶点数据更新方法是预先获取setVertexArray()所用的数组数据,并对其进行更新.但是对于开启显示列表支持的几何体(这是默认的情况)来说,有一个问题需要特别需要引起注意,即显示列表中的 ...
- DOM结构及优化
1.DOM树中三种常见的DOM节点: 1>元素节点:上图中<a>,<h1>等都是元素节点,即标签 2>文本节点:向用户展示的内容,如...中的"文档标题& ...
- lua中pairs 和 ipairs 的区别
1.table中存储值的时候,是按照顺序存储的,存储 k-v 的时候,是按照 k 的哈希值存储的. 2.ipairs --- 只能输出 table 中的值,并且不可输出nil,遇到 ni l就退出 p ...
- (转)winform之RichTextBox
RichTextBox是一种可用于显示.输入和操作格式文本,除了可以实现TextBox的所有功能,还能提供富文本的显示功能. 控件除具有TextBox 控件的所有功能外,还能设定文字颜色.字体和段落格 ...
- layui 日期插件onchange事件失效的方法
laydate.render({ elem:'#text1',//制定元素 type:'date', //range:true,//开启左右面板 min:'2017-09-1',// max:'201 ...
- Enable SMB2 on the Client
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanmanWorkstation edit DependOnService and add ...