一致性哈希做负载均衡,基于dubbo的简化版本,超级简单容易理解!!!
一致性哈希算法原理以及做分布式存储。一定先看:一致性哈希算法
dubbo提供了四种负载均衡实现:权重随机算法,最少活跃调用数算法,一致性哈希算法,加权轮询算法。
本文基于开源项目:guide-rpc-framework的一致性哈希算法做的负载均衡,这个项目的负载均衡是dubbo一致性哈希的简化版。
代码如下:
/**
* refer to dubbo consistent hash load balance: https://github.com/apache/dubbo/blob/2d9583adf26a2d8bd6fb646243a9fe80a77e65d5/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/loadbalance/ConsistentHashLoadBalance.java
*
* @author RicardoZ
* @createTime 2020年10月20日 18:15:20
*/
@Slf4j
public class ConsistentHashLoadBalance extends AbstractLoadBalance {
private final ConcurrentHashMap<String, ConsistentHashSelector> selectors = new ConcurrentHashMap<>();
@Override
protected String doSelect(List<String> serviceAddresses, String rpcServiceName) {
int identityHashCode = System.identityHashCode(serviceAddresses);
ConsistentHashSelector selector = selectors.get(rpcServiceName);
// check for updates
if (selector == null || selector.identityHashCode != identityHashCode) {
selectors.put(rpcServiceName, new ConsistentHashSelector(serviceAddresses, 160, identityHashCode));
selector = selectors.get(rpcServiceName);
}
return selector.select(rpcServiceName);
}
static class ConsistentHashSelector {
private final TreeMap<Long, String> virtualInvokers;
private final int identityHashCode;
ConsistentHashSelector(List<String> invokers, int replicaNumber, int identityHashCode) {
this.virtualInvokers = new TreeMap<>();
this.identityHashCode = identityHashCode;
for (String invoker : invokers) {
for (int i = 0; i < replicaNumber / 4; i++) {
byte[] digest = md5(invoker + i);
for (int h = 0; h < 4; h++) {
long m = hash(digest, h);
virtualInvokers.put(m, invoker);
}
}
}
}
static byte[] md5(String key) {
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
byte[] bytes = key.getBytes(StandardCharsets.UTF_8);
md.update(bytes);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e.getMessage(), e);
}
return md.digest();
}
static long hash(byte[] digest, int idx) {
return ((long) (digest[3 + idx * 4] & 255) << 24 | (long) (digest[2 + idx * 4] & 255) << 16 | (long) (digest[1 + idx * 4] & 255) << 8 | (long) (digest[idx * 4] & 255)) & 4294967295L;
}
public String select(String rpcServiceName) {
byte[] digest = md5(rpcServiceName);
return selectForKey(hash(digest, 0));
}
public String selectForKey(long hashCode) {
Map.Entry<Long, String> entry = virtualInvokers.tailMap(hashCode, true).firstEntry();
if (entry == null) {
entry = virtualInvokers.firstEntry();
}
return entry.getValue();
}
}
}
客户端要调用服务的名字即为rpcServiceName,客户端要通过服务名发送请求之前,先进行负载均衡,通过负载均衡找到合适的服务器ip地址,然后依据此ip地址发送请求。
分析ConsistentHashSelector这个类可以将多个服务器ip地址放到环形hash空间上,然后通过服务名找到一个ip地址。
那什么存储数据呢?virtualInvokers用来模拟环形hash空间用来放置ip地址和服务名。
invokers为ip地址列表。
ConsistentHashSelector(List<String> invokers, int replicaNumber, int identityHashCode) {
this.virtualInvokers = new TreeMap<>();
this.identityHashCode = identityHashCode;
// 对于每一个ip地址
for (String invoker : invokers) {
// 针对每一个ip地址创建(replicaNumber / 4)个重复节点
for (int i = 0; i < replicaNumber / 4; i++) {
byte[] digest = md5(invoker + i);
// 针对每个一个重复节点将其等间隔的分布在环形hash空间上
for (int h = 0; h < 4; h++) {
// 计算节点hash值
long m = hash(digest, h);
// 将节点放到环形hash空间上
virtualInvokers.put(m, invoker);
}
}
}
}
我们知道TreeMap不是环形的,他就是一个用来放东西的容器,这里只是为了迎合一致性哈希算法的概念中的环形hash空间。
比如说replicaNumber的值为8,那么就会有两个重复节点,对于每个重复节点又会计算4个hash值,如此一来在环形hash空间上也就是TreeMap上,会有2乘4个也就是8个value相同(value就是ip地址),而hash值不同的键值对。
如下图(为了简单起见,hash值就都是两位数以内了哈~~)

现在我们就把这些服务器的ip地址都安置好了。
那么接下来就是让rpcServiceName依据自己的hash值顺时针在环形hash空间上找到第一个离他最近的ip地址啦。

代码如何实现的顺时针寻找第一个最近的ip地址节点呢?
public String selectForKey(long hashCode) {
Map.Entry<Long, String> entry = virtualInvokers.tailMap(hashCode, true).firstEntry();
if (entry == null) {
entry = virtualInvokers.firstEntry();
}
return entry.getValue();
}
通过TreeMap的tailMap方法可以进行顺时针寻找。通过firstEntry可以找到第一个最近的ip地址节点。
如此对于不同的服务则会根据自己的hash值去顺时针寻找离自己最近的服务器的ip地址。
一致性哈希做负载均衡,基于dubbo的简化版本,超级简单容易理解!!!的更多相关文章
- tomcat 7 用mod_jk做 负载均衡
在Win7中使用apache为tomcat做负载均衡,各组件及版本如下: 两个tomcat v 7.0.57 一个apache v 2.2.14 一个mod_jk v 1.2.33(for windo ...
- RabbitMQ3.6.3集群搭建+HAProxy1.6做负载均衡
目录 [TOC] 1.基本概念 1.1.RabbitMQ集群概述 通过 Erlang 的分布式特性(通过 magic cookie 认证节点)进行 RabbitMQ 集群,各 RabbitMQ 服 ...
- RabbitMQ3.6.3集群搭建+HAProxy1.6做负载均衡
目录 目录 1.基本概念 1.1.RabbitMQ集群概述 1.2.软件负载均衡器HAProxy 2.RabbitMQ的配置步骤 2.1.安装 Erlang.RabbitMQ 2.2.修改 /etc/ ...
- 消费者用nginx做负载均衡,提供者用zookeeper自带功能实现负载均衡
公司的项目基于阿里的Dubbo微服务框架开发.为了符合相关监管部门的安全要求,公司购买了华东1.华东2两套异地服务器,一套是业务服务器,一套是灾备服务器.准备在这两套服务器上实现Dubbo的分布式服务 ...
- 死磕nginx系列--使用nginx做负载均衡
使用nginx做负载均衡的两大模块: upstream 定义负载节点池. location 模块 进行URL匹配. proxy模块 发送请求给upstream定义的节点池. upstream模块解读 ...
- K2使用Nginx做负载均衡
K2使用Nginx做负载均衡 K2目前是支持Load Balancing这种方式,来做负载均衡,也可以使用F5来做负载均衡,但这次我使用nginx来实现K2的负载均衡 下载nginx 请下载nginx ...
- tomcat结合nginx或apache做负载均衡及session绑定
1.tomcat结合nginx做负载均衡,session绑定 nginx:192.168.223.136 tomcat:192.168.223.146:8081,192.168.223.146:8 ...
- nginx反向代理做负载均衡以及使用redis实现session共享配置详解
1.为什么要用nginx做负载均衡? 首先我们要知道用单机tomcat做的网站,比较理想的状态下能够承受的并发访问在150到200, 按照并发访问量占总用户数的5%到10%技术,单点tomcat的用户 ...
- 在Linux上使用Nginx为Solr集群做负载均衡
在Linux上使用Nginx为Solr集群做负载均衡 在Linux上搭建solr集群时需要用到负载均衡,但测试环境下没有F5 Big-IP负载均衡交换机可以用,于是先后试了weblogic的proxy ...
随机推荐
- javascript中的内置对象和数据结构
目录 简介 基础类型 undefined Boolean和Boolean对象 Number和BigInt String Symbol null Object Function Date Array K ...
- python进阶(6)深拷贝和浅拷贝
深拷贝和浅拷贝 不管对于浅拷贝.还是深拷贝,针对不可变对象str.int.tuple(有点特殊).boolean,它的内存地址是不变的,拷贝的仅仅是值 import copy a = 1 b = co ...
- MySQL 导出 select 结果集
reference: https://blog.csdn.net/huaishuming/article/details/74762652法一:SELECT * from jc_archives wh ...
- Traefik-v2.x快速入门
一.概述 traefik 与 nginx 一样,是一款优秀的反向代理工具,或者叫 Edge Router.至于使用它的原因则基于以下几点 无须重启即可更新配置 自动的服务发现与负载均衡 与 docke ...
- 后端程序员之路 2、nginx、php
nginx是由俄罗斯人开发的一种实现web服务器的工具,主要是为俄罗斯的第三大门户网站实现反向代理加速的服务器. Linux(CentOS)下,下载安装Nginx并配置 - jtlgb - 博客园ht ...
- 剑指 Offer 32 - I. 从上到下打印二叉树 + 层次遍历二叉树
剑指 Offer 32 - I. 从上到下打印二叉树 Offer_32_1 题目描述 解题思路 这题属于简单题,考察的是我们对二叉树以及层次遍历的方法. 这里只需要使用简单的队列即可完成二叉树的层次遍 ...
- Java数据类型拓展
public class Demo03 { public static void main(String[] args) { //整数拓展: 二进制0b 十进制 八进制0 十六进制0x int i = ...
- crontab任务重复执行?不执行?不按照配置执行?大概率是配置出错了!!!
在使用crontab配置定时任务是,容易大意出错的配置记录,有温度的文章分享,有态度的日常记录- 一.情景1 设置每天凌晨执行某一任务,结果发现凌晨0点没分钟都执行了一次,我的天!!! 1.分析原因可 ...
- springboot注解之@Import @Conditional @ImportResource @ConfigurationProperties @EnableConfigurationProperties
1.包结构 2.主程序类 1 @SpringBootApplication(scanBasePackages={"com.atguigu"}) 2 public class Mai ...
- MyBatis-Plus【踩坑记录01】
不要使用Mybatis原生的SqlSessionFactory,而应使用MybatisSqlSessionFactory. 原因 依赖关系如下 因此会在使用Mybaits-Plus时默认的时Mybat ...