什么是一致性Hash
一致性Hash就是将整个hash值空间按照顺时针方向形成一个虚拟的环,整个环状结构就称之为Hash环。那为什么叫做一致性Hash环?一致性是由于Hash环应用场景一般在分布式应用服务中,各个服务提供者分布在hash环中,当某一个服务出现问题的时候,我们还是能够保证绝大多数服务保持正常使用,对于绝大多数数据前后是一致的。

hash函数 VS 一致性hash
我们以分布式应用服务负载均衡来提供分析场景
1、hash函数路由到服务器,需要根据服务提供者数量取模,比如 hash(ip) % serverNum。如果服务数量变动,会导致绝大多数服务路由异常。
2、一致性hash,我们均匀的将服务提供者ip分布在环上,当我们获取服务ip时候会根据hash值找到第一个大于当前hash的节点即为路由ip。如果服务数量变动,也只会影响部分少量的数据,大部分数据还是正确的路由地址。
综上所述,一致性hash函数在分布式数据缓存的应用场景下完全碾压普通hash函数。

一致性hash原理
我们以分布式应用服务负载均衡来提供分析场景
1、由于int的存储位数为32位,那么可以保存2^32个数。我们的一致性hash通过固定的hash算法的结果用int保存,那么hash环的取值范围则为 0~2^32 -1。
2、一致性hash首先会将服务器ip通过hash运算后放置在虚拟环上
比如我们三个服务ip1~3 【10.10.10.202,10.10.10.203,10.10.10.204】

3、我们在实际业务中可以通过用户ID等其他信息计算出hash值,然后找到hash环上大于当前值的第一个节点,该节点则为目标路由节点。
比如:user1\user3的hash值小于ip1的hash,那么ip1节点就这两个用户的实际路由节点

4、当前的hash环上的ip节点都是实际节点,如果全部为实节点会有一个问题,就是如果某个服务掉线会将掉线服务请求全部打在后续节点,就会造成路由不均衡,严重情况会导致后续服务雪崩。那么,如果解决这个问题呢?我们可以采用虚节点,就是在各个实际节点处分散为多个虚拟节点,各个hash路由也都映射在虚节点上,这样就能够做大限度保证数据的均衡性。
比如:ip2服务宕机,本来该路由到ip2的请求全部会打在ip3

我们象征性添加9个虚拟节点,每个ip增加三个虚节点,每个虚节点都均匀的分散在环上

ip2服务不可用,直接将ip2的虚节点路由均衡的打在了ip1、ip3上面

小试牛刀
我们直接实战一致性hash环虚拟节点这中方式,而且在实际业务场景中我们也是优先选用该种方式。
我们定义三个服务节点ip,分别为:ip1~3【10.10.10.202,10.10.10.203,10.10.10.204】。
为了演示方便我们每个实节点增加三个虚拟节点分别为 ip13-VN13。

1、一致性hash带虚拟节点工具类,提供初始化hash环、获取hash值、获取目标路由等方法

/*** 一致性hash函数--虚拟节点* @author senfel* @version 1.0* @date 2023/1/17 10:41*/
@Slf4j
public class ConsistentHashVirtualNode {/*** 实际节点虚节点数量*/private static int virtualNodeNum = 3;/*** 实际节点集合* 本次测试我们用hash(ip)作为环上实际节点*/private static List<String> realNodeList = new ArrayList<>();/*** 虚拟节点集合* map key-虚拟节点hash*     value-虚拟节点*/private static SortedMap<Integer,String> virtualNodeMap = new TreeMap<Integer,String>();/*** 计算hash值* 计算方法很多,普通hashcode相近数据分布不明显,我们采用FNV32进行测试* 1. 加法Hash;* 2. 位运算Hash;* 3. 乘法Hash;* 4. 除法Hash;* 5. 查表Hash;* 6. 混合Hash* 目前比较流行的是乘法hash FNV32* @param str* @author senfel* @date 2023/1/17 10:54* @return int*/private static int getHash(String str){//return Math.abs(str.hashCode()); hash值分布不明显弃用final int p = 16777619;int hash = (int)2166136261L;for (int i = 0; i < str.length(); i++)hash = (hash ^ str.charAt(i)) * p;hash += hash << 13;hash ^= hash >> 7;hash += hash << 3;hash ^= hash >> 17;hash += hash << 5;// 如果算出来的值为负数则取其绝对值if (hash < 0)hash = Math.abs(hash);return hash;}/*** 初始化hash环* @param ipArray* @author senfel* @date 2023/1/17 10:48* @return void*/public static void initHashNode(String[] ipArray){if(null == ipArray || ipArray.length == 0){return;}//将ip写入实际节点for(int i=0;i<ipArray.length;i++){realNodeList.add(ipArray[i]);}//获取各个节点的虚拟节点,将虚拟节点加入hash环realNodeList.forEach(ip ->{for(int j=1;j<=virtualNodeNum;j++){String virtualNode = ip+"&VN"+j;//计算虚拟节点hashint virtualHash = getHash(virtualNode);//将节点放置在环上virtualNodeMap.put(virtualHash,virtualNode);log.error("一致性hash环初始化-虚拟节点:{}加入hash环,当前节点hash值为:{}",virtualNode,virtualHash);}});}/*** 我们实战场景是根据用户ID进行hash,然后找到路由ip* @param userId* @author senfel* @date 2023/1/17 10:57* @return java.lang.String*/public static String getServerIp(int userId){//获取用户hashint hash = getHash(userId + "");//根据用户hash获取hash环中大于当前值的后续节点SortedMap<Integer,String> sortedMap = virtualNodeMap.tailMap(hash);int firstKey = 0;String nodeIp = null;if(sortedMap == null){//没有大于当前hash数据,应该是等于最小节点hash,直接取第一个节点即可firstKey = virtualNodeMap.firstKey();//获取到当前节点的ipnodeIp = virtualNodeMap.get(firstKey);}else{//直接获取节点firstKey = sortedMap.firstKey();nodeIp = sortedMap.get(firstKey);}return nodeIp;}}

2、测试方法,模拟服务注册初始化hash环,模拟用户访问路由

 public static void main(String[] args) {//测试ip 10.10.10.202,10.10.10.203,10.10.10.204String[] ipArray = {"10.10.10.202","10.10.10.203","10.10.10.204"};initHashNode(ipArray);//测试用户模拟9个用户List<Integer> userList = new ArrayList<>();int userId = 2023011700;for(int i=0;i<9;i++){userId += i * 100;userList.add(userId);}//模拟用户访问for (Integer id : userList) {String serverIp = getServerIp(id);String realIp = null != serverIp ? serverIp.substring(0, serverIp.indexOf("&")) : null;log.error("用户{}访问系统,路由到虚拟节点:{},实际路由ip为:{}",id,serverIp,realIp);}}

3、查看测试结果,展示初始化hash环各个虚拟节点加入、用户访问路由真实ip

hash环初始化

  • 一致性hash环初始化-虚拟节点:10.10.10.202&VN1加入hash环,当前节点hash值为:1417355037
  • 一致性hash环初始化-虚拟节点:10.10.10.202&VN2加入hash环,当前节点hash值为:1001023951
  • 一致性hash环初始化-虚拟节点:10.10.10.202&VN3加入hash环,当前节点hash值为:744395883
  • 一致性hash环初始化-虚拟节点:10.10.10.203&VN1加入hash环,当前节点hash值为:573863409
  • 一致性hash环初始化-虚拟节点:10.10.10.203&VN2加入hash环,当前节点hash值为:197370571
  • 一致性hash环初始化-虚拟节点:10.10.10.203&VN3加入hash环,当前节点hash值为:1087274216
  • 一致性hash环初始化-虚拟节点:10.10.10.204&VN1加入hash环,当前节点hash值为:944575377
  • 一致性hash环初始化-虚拟节点:10.10.10.204&VN2加入hash环,当前节点hash值为:2022065171
  • 一致性hash环初始化-虚拟节点:10.10.10.204&VN3加入hash环,当前节点hash值为:1365766942

用户访问ip路由

  • 用户2023011700访问系统,路由到虚拟节点:10.10.10.204&VN3,实际路由ip为:10.10.10.204
  • 用户2023011800访问系统,路由到虚拟节点:10.10.10.204&VN1,实际路由ip为:10.10.10.204
  • 用户2023012000访问系统,路由到虚拟节点:10.10.10.204&VN2,实际路由ip为:10.10.10.204
  • 用户2023012300访问系统,路由到虚拟节点:10.10.10.203&VN1,实际路由ip为:10.10.10.203
  • 用户2023012700访问系统,路由到虚拟节点:10.10.10.204&VN1,实际路由ip为:10.10.10.204
  • 用户2023013200访问系统,路由到虚拟节点:10.10.10.204&VN3,实际路由ip为:10.10.10.204
  • 用户2023013800访问系统,路由到虚拟节点:10.10.10.202&VN3,实际路由ip为:10.10.10.202
  • 用户2023014500访问系统,路由到虚拟节点:10.10.10.203&VN1,实际路由ip为:10.10.10.203
  • 用户2023015300访问系统,路由到虚拟节点:10.10.10.204&VN2,实际路由ip为:10.10.10.204

总结:
一致性hash能够满足分布式服务路由场景,服务的波动对用户影响不大。为了达到各个路由真正的负载均衡,建议采用带有虚拟节点的一致性hash函数。

分布式应用解决方案之一致性Hash相关推荐

  1. nginx负载均衡之一致性Hash方式

    ip_hash方式 关于nginx的负载均衡,大家都知道有一个ip_hash的方式,就是将客户端的ip取hash值,然后根据服务器 的数量取模,得出的值就是最后被路由到的服务器(服务器从0开始数),但 ...

  2. hash,bloomfilter,分布式一致性hash

    场景 使用word 文档时,判断某个单词是否拼写正确 垃圾邮件过滤算法 Redis缓存穿透 bitcoin core中交易校验 需求 从海量数据中查询某个字符串是否存在? 平衡二叉搜索树 增删改查时间 ...

  3. 算法:五分钟了解一致性hash算法

    五分钟了解一致性hash算法 前言 一致性哈希算法的设计目标是为了解决因特网中的热点问题,现在也被广泛应用在分布式系统中. 比如针对负载均衡问题,对hash值取模的算法扩展性差,当增加或者减少服务器时 ...

  4. 一致性hash算法 - consistent hashing

    参考: http://yacare.iteye.com/blog/1973022 1.   情景分析 前一篇博文分析了HashMap源码,HashMap在许多场景中作为存储数据的不二选择. 但是否使用 ...

  5. 对一致性Hash算法,Java代码实现的深入研究

    一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...

  6. 一致性 Hash 在负载均衡中的应用

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:干掉 Navicat:这个 IDEA 的兄弟真香!个人原创100W+访问量博客:点击前往,查看更多 转自:Mar ...

  7. 一致性hash算法使用

    一.概述 1.我们的memcache客户端(这里我看的spymemcache的源码),使用了一致性hash算法ketama进行数据存储节点的选择.与常规的hash算法思路不同,只是对我们要存储数据的k ...

  8. 五分钟了解一致性hash算法!

    前言 一致性哈希算法的设计目标是为了解决因特网中的热点问题,现在也被广泛应用在分布式系统中. 比如针对负载均衡问题,对hash值取模的算法扩展性差,当增加或者减少服务器时,映射关系可能会出现问题,采用 ...

  9. Java教程分享:五分钟了解一致性hash算法

    前言 一致性哈希算法的设计目标是为了解决因特网中的热点问题,现在也被广泛应用在分布式系统中. 比如针对负载均衡问题,对hash值取模的算法扩展性差,当增加或者减少服务器时,映射关系可能会出现问题,采用 ...

最新文章

  1. IDC预测2022年全球智能家居连接设备市场规模将达10亿台!
  2. 使用ps命令输出进程列表--用Enki学Linux系列(17)
  3. 非递归快速排序php,快排序的非递归实现(原创)
  4. 如何安全的在不同工程间安全地迁移asset数据?三种方法
  5. toLua关于委托没有注册的解决方案
  6. 微软在 Linux 虚拟机偷偷安装Azure App,后修复严重漏洞但Linux虚拟机难以修复
  7. 【http协议2】 深入理解HTTP协议
  8. python 回归方程及回归系数的显著性检验_回归方程及回归系数的显著性检验_stata显著性检验...
  9. win10插上耳机还外放解决解决方法
  10. GDAL(Geospatial Data Abstraction Library )简介
  11. Java+阿里云手机验证码发送和验证
  12. php映射脚本,代替php脚本
  13. 新手期货开户的时候需要准备什么呢?
  14. 手机必备四款提高工作效率APP,每一个都是黑科技!
  15. uni-app图片如何设置双指放大缩小
  16. 应用程序日志管理工具
  17. RGB TO ARGB
  18. 从阿尔法元到人工智能会取代你的工作吗?
  19. SAP HANA基本数据类型
  20. 百度没钱了!百度有钱联盟平台暂停推广

热门文章

  1. 用Python制作动态二维码,一行代码就做到了
  2. 【图像分类】2022-CMT CVPR
  3. 【00】processing-历史(中文)
  4. Elasticsearch解决只能查询10000条数据以及查询的total为10000条的解决方案
  5. mysql sin度数正玄值_JavaScript用Math.sin()求正弦值
  6. linux后台开发之什么是缓存系统三座大山
  7. 正则表达式在线生成器(txt2re)
  8. 前端文件上传的实现(非常详细)
  9. 仿京东、饿了么 左右联动菜单列表自定义View
  10. 计算机的外围设备简介