一致性哈希算法原理以及做分布式存储。一定先看:一致性哈希算法

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的简化版本,超级简单容易理解!!!的更多相关文章

  1. 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 ...

  2. RabbitMQ3.6.3集群搭建+HAProxy1.6做负载均衡

    目录 [TOC] 1.基本概念 1.1.RabbitMQ集群概述   通过 Erlang 的分布式特性(通过 magic cookie 认证节点)进行 RabbitMQ 集群,各 RabbitMQ 服 ...

  3.  RabbitMQ3.6.3集群搭建+HAProxy1.6做负载均衡

    目录 目录 1.基本概念 1.1.RabbitMQ集群概述 1.2.软件负载均衡器HAProxy 2.RabbitMQ的配置步骤 2.1.安装 Erlang.RabbitMQ 2.2.修改 /etc/ ...

  4. 消费者用nginx做负载均衡,提供者用zookeeper自带功能实现负载均衡

    公司的项目基于阿里的Dubbo微服务框架开发.为了符合相关监管部门的安全要求,公司购买了华东1.华东2两套异地服务器,一套是业务服务器,一套是灾备服务器.准备在这两套服务器上实现Dubbo的分布式服务 ...

  5. 死磕nginx系列--使用nginx做负载均衡

    使用nginx做负载均衡的两大模块: upstream 定义负载节点池. location 模块 进行URL匹配. proxy模块 发送请求给upstream定义的节点池. upstream模块解读 ...

  6. K2使用Nginx做负载均衡

    K2使用Nginx做负载均衡 K2目前是支持Load Balancing这种方式,来做负载均衡,也可以使用F5来做负载均衡,但这次我使用nginx来实现K2的负载均衡 下载nginx 请下载nginx ...

  7. tomcat结合nginx或apache做负载均衡及session绑定

    1.tomcat结合nginx做负载均衡,session绑定 nginx:192.168.223.136   tomcat:192.168.223.146:8081,192.168.223.146:8 ...

  8. nginx反向代理做负载均衡以及使用redis实现session共享配置详解

    1.为什么要用nginx做负载均衡? 首先我们要知道用单机tomcat做的网站,比较理想的状态下能够承受的并发访问在150到200, 按照并发访问量占总用户数的5%到10%技术,单点tomcat的用户 ...

  9. 在Linux上使用Nginx为Solr集群做负载均衡

    在Linux上使用Nginx为Solr集群做负载均衡 在Linux上搭建solr集群时需要用到负载均衡,但测试环境下没有F5 Big-IP负载均衡交换机可以用,于是先后试了weblogic的proxy ...

随机推荐

  1. 深入剖析 ConcurrentHashMap

    自建博客地址:https://bytelife.net,欢迎访问! 本文为博客自动同步文章,为了更好的阅读体验,建议您移步至我的博客 本文作者: Jeffrey 本文链接: https://bytel ...

  2. Redis缓存中的常见问题

    缓存穿透:是指查询一个Redis和数据库中都不存在的数据. 问题:查询一个Redis和数据库中都不存在的数据,大量请求去访问数据库,导致数据库宕机. 解决办法: 1.根据id查询,如果id是自增的,将 ...

  3. (数据科学学习手札110)Python+Dash快速web应用开发——静态部件篇(下)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  4. 面试准备——计算机网络(https)

    一.为什么要提出HTTPS? HTTP的缺点: 明文通信.不加密,可能被窃听. 无身份验证,可能遭遇伪装. 无法证明报文的完整性,可能被篡改. 二.HTTPS = HTTP+加密(防窃听)+认证(防伪 ...

  5. 初识Java多线程

    一.多线程概述 1.1.程序.进程.线程概念 1)程序 是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象. 2)进程 是指一个内存中运行的应用程序,每个进程都有一个独立的 ...

  6. OpenCV 之 角点检测

    角点 (corners) 的定义有两个版本:一是 两条边缘的交点,二是 邻域内具有两个主方向的特征点. 一般而言,角点是边缘曲线上曲率为极大值的点,或者 图像亮度发生剧烈变化的点.例如,从人眼角度来看 ...

  7. Java__包机制__用户输入

    包机制 包机制的存在是为了解决当定义了多个类的时候,可能会出现类名重复的问题,包机制的存在可以解决这一冲突. 包的作用 把功能相似的类或者相关接口组织在同一个包里,方便再查找. 包名可以避免名字冲突. ...

  8. NIO三大组件之Buffer

    什么是Buffer Buffer(这里并不是特指Buffer类)是一个存储数据的容器,与数组类似(其实底层依旧是用数组的结构来存储数据),但不同的是,Buffer对象提供了一组更有效的方法去进行写入和 ...

  9. canvas-修改图片亮度

    canvas操作-修改图片亮度 目录 canvas操作-修改图片亮度 图片亮度的概念 下面用ps截图举一个例子: 调整图片亮度的方案 实现方案一 从RGB到HSV的转换 转换的公式 javascrip ...

  10. gtk中构件添加背景图

    在gtk中我们总想要去给构件添加背景图,具体函数代码如下 void chang_background(GtkWidget *widget, int w, int h, const gcha r *pa ...