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

dubbo提供了四种负载均衡实现:权重随机算法最少活跃调用数算法一致性哈希算法加权轮询算法

本文基于开源项目:guide-rpc-framework的一致性哈希算法做的负载均衡,这个项目的负载均衡是dubbo一致性哈希的简化版。

代码如下:

  1. /**
  2. * 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
  3. *
  4. * @author RicardoZ
  5. * @createTime 2020年10月20日 18:15:20
  6. */
  7. @Slf4j
  8. public class ConsistentHashLoadBalance extends AbstractLoadBalance {
  9. private final ConcurrentHashMap<String, ConsistentHashSelector> selectors = new ConcurrentHashMap<>();
  10. @Override
  11. protected String doSelect(List<String> serviceAddresses, String rpcServiceName) {
  12. int identityHashCode = System.identityHashCode(serviceAddresses);
  13. ConsistentHashSelector selector = selectors.get(rpcServiceName);
  14. // check for updates
  15. if (selector == null || selector.identityHashCode != identityHashCode) {
  16. selectors.put(rpcServiceName, new ConsistentHashSelector(serviceAddresses, 160, identityHashCode));
  17. selector = selectors.get(rpcServiceName);
  18. }
  19. return selector.select(rpcServiceName);
  20. }
  21. static class ConsistentHashSelector {
  22. private final TreeMap<Long, String> virtualInvokers;
  23. private final int identityHashCode;
  24. ConsistentHashSelector(List<String> invokers, int replicaNumber, int identityHashCode) {
  25. this.virtualInvokers = new TreeMap<>();
  26. this.identityHashCode = identityHashCode;
  27. for (String invoker : invokers) {
  28. for (int i = 0; i < replicaNumber / 4; i++) {
  29. byte[] digest = md5(invoker + i);
  30. for (int h = 0; h < 4; h++) {
  31. long m = hash(digest, h);
  32. virtualInvokers.put(m, invoker);
  33. }
  34. }
  35. }
  36. }
  37. static byte[] md5(String key) {
  38. MessageDigest md;
  39. try {
  40. md = MessageDigest.getInstance("MD5");
  41. byte[] bytes = key.getBytes(StandardCharsets.UTF_8);
  42. md.update(bytes);
  43. } catch (NoSuchAlgorithmException e) {
  44. throw new IllegalStateException(e.getMessage(), e);
  45. }
  46. return md.digest();
  47. }
  48. static long hash(byte[] digest, int idx) {
  49. 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;
  50. }
  51. public String select(String rpcServiceName) {
  52. byte[] digest = md5(rpcServiceName);
  53. return selectForKey(hash(digest, 0));
  54. }
  55. public String selectForKey(long hashCode) {
  56. Map.Entry<Long, String> entry = virtualInvokers.tailMap(hashCode, true).firstEntry();
  57. if (entry == null) {
  58. entry = virtualInvokers.firstEntry();
  59. }
  60. return entry.getValue();
  61. }
  62. }
  63. }

客户端要调用服务的名字即为rpcServiceName,客户端要通过服务名发送请求之前,先进行负载均衡,通过负载均衡找到合适的服务器ip地址,然后依据此ip地址发送请求。

分析ConsistentHashSelector这个类可以将多个服务器ip地址放到环形hash空间上,然后通过服务名找到一个ip地址。

那什么存储数据呢?virtualInvokers用来模拟环形hash空间用来放置ip地址和服务名。

invokers为ip地址列表。

  1. ConsistentHashSelector(List<String> invokers, int replicaNumber, int identityHashCode) {
  2. this.virtualInvokers = new TreeMap<>();
  3. this.identityHashCode = identityHashCode;
  4. // 对于每一个ip地址
  5. for (String invoker : invokers) {
  6. // 针对每一个ip地址创建(replicaNumber / 4)个重复节点
  7. for (int i = 0; i < replicaNumber / 4; i++) {
  8. byte[] digest = md5(invoker + i);
  9. // 针对每个一个重复节点将其等间隔的分布在环形hash空间上
  10. for (int h = 0; h < 4; h++) {
  11. // 计算节点hash值
  12. long m = hash(digest, h);
  13. // 将节点放到环形hash空间上
  14. virtualInvokers.put(m, invoker);
  15. }
  16. }
  17. }
  18. }

我们知道TreeMap不是环形的,他就是一个用来放东西的容器,这里只是为了迎合一致性哈希算法的概念中的环形hash空间。

比如说replicaNumber的值为8,那么就会有两个重复节点,对于每个重复节点又会计算4个hash值,如此一来在环形hash空间上也就是TreeMap上,会有2乘4个也就是8个value相同(value就是ip地址),而hash值不同的键值对。

如下图(为了简单起见,hash值就都是两位数以内了哈~~)

现在我们就把这些服务器的ip地址都安置好了。

那么接下来就是让rpcServiceName依据自己的hash值顺时针在环形hash空间上找到第一个离他最近的ip地址啦。

代码如何实现的顺时针寻找第一个最近的ip地址节点呢?

  1. public String selectForKey(long hashCode) {
  2. Map.Entry<Long, String> entry = virtualInvokers.tailMap(hashCode, true).firstEntry();
  3. if (entry == null) {
  4. entry = virtualInvokers.firstEntry();
  5. }
  6. return entry.getValue();
  7. }

通过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. Linux的启动过程及init进程

    Linux下有三个特殊进程: idle进程(pid=0)idle进程其前身是系统创建的第一个进程,0号进程,也唯一一个没有通过fork()或者kernel_thread产生的进程,由系统自动创建,运行 ...

  2. IO、NIO、BIO的区别

    我们首先得明白什么是同步,异步,阻塞,非阻塞,只有这几个单个概念理解清楚了,然后在组合理解起来,就相对比较容易了. IO模型主要分类: 同步(synchronous) IO和异步(asynchrono ...

  3. std和stl的关系

    [前言]在写程序时,虽然一直这么用,有点疑惑为甚么引入了头文件.h还要在加上using namespace std?例如: 1 #include<iostream> 2 using nam ...

  4. Nginx配置翻译

    Windows 格式 server { listen 82; server_name localhost; root "D:/testfile/"; location / { in ...

  5. 全局解决Vue跳转相同路由导致报错的问题

    大家使用Vue做开发的时候应该都遇到过这个问题,就是某个页面下调用this.$router.push(path),而path指向的页面和当前页面是同一页面时,就会发生报错,vue-router会提示你 ...

  6. PCA——主成分分析

    PCA(Principal Components Analysis)主成分分析是一个简单的机器学习算法,利用正交变换把由线性相关变量表示的观测数据转换为由少量线性无关比变量表示的数据,实现降维的同时尽 ...

  7. Kafka SASL ACL配置踩坑总结

    源起:工程现阶段中间件采用的是kafka.满足了大数据的高吞吐,项目间的解耦合,也增强了工程的容错率与扩展性.但是在安全这一块还有漏洞,kafka集群中,只要网站内的任何人知道kafka集群的ip与t ...

  8. 还在用crontab? 分布式定时任务了解一下

    前言 日常任务开放中,我们会有很多异步.批量.定时.延迟任务要处理,go-zero中有 go-queue,推荐使用 go-queue 去处理,go-queue 本身也是基于 go-zero 开发的,其 ...

  9. TensorFlow学习(2)

    TensorFlow学习(2) 一.jupyter notebook的安装和使用 1. 什么是jupyter notebook jupyter notebook(http://jupyter.org/ ...

  10. HDU_3333 Turing Tree 【线段树 + 离散化】

    一.题目 Turing Tree 二.分析 这题主要还是在区间的处理上. 为了保证区间内的数没有重复的,那么可以对区间按右端点从小到大排序,这样对原数组处理时,尽量保证不重复的元素靠右(可以假设右端点 ...