一致性Hash问题及解决方案

Hash算法的应用场景

  1. 请求的负载均衡
    Nginx的ip_hash策略可以在客户端ip不发生变化的情况下,将其发出的请求始终路由到同一个目标服务器上,实现会话粘滞,避免处理session共享问题。
  • 如果没有ip_hash策略,可以通过维护一张映射表的方式来实现会话粘滞:
    • 映射表存储的是客户端ip或者session与具体目标服务器的映射关系 <ip,server>

      • 缺点:
        1. 客户端很多的情况下,映射表非常大,浪费内存空间
        2. 客户端上下线、目标服务器上下线都会导致重新维护映射表,维护成本大
    • 如果使用Hash算法,可以对ip或者session计算hash值,hash值与服务器数量进行取模运算,得到的值就是当前请求应该被路由到的服务器编号。由此,同一个ip发送过来的请求就可以路由到同一个目标服务器,实现会话粘滞。

  1. 分布式存储
    有 Redis1、Redis2、Redis3 三台Redis服务器,可以针对key进行hash处理 Hash(key)%3 = index 使用余数锁定存储的具体服务器节点。

普通Hash算法存在的问题

以ip_hash为例,假定用户ip固定不变,当后端服务器有一个宕机,数量由3变为2,那么之前所有用户的求模都需要重新计算。
如果在生产环境中,后台服务器有很多台,客户端也有很多个,出现这种问题的影响是很大的。服务器的扩缩容都会出现这样的问题,大量的用户请求被路由到其他的目标服务器处理,用户在原来服务器的会话都将丢失。

普通Hash算法:

// 定义客户端IP
String[] clients = {"192.168.31.121", "192.168.3.21", "192.168.1.11"};// 定义服务器数量
int server = 5;// hash(ip) % server数量 = index
for (String client : clients) {int hashCode = Math.abs(client.hashCode());int index = hashCode % server;System.out.println("客户端:\t" + client + "\t 分配到了服务器 " + (index + 1) + " 上");
}

一致性Hash算法的思路

  1. 首先有一条直线,直线开头和结尾分别定为1和232-1,这相当于一个地址
  2. 对于这样一条直线,首尾相连构成一个闭环,这样的圆环称为Hash环
  3. 把服务器IP或者主机名求Hash值然后对应到Hash环上,针对客户端IP求Hash值,对应到环上的某个位置,然后按照顺时针的方向找最近的服务器节点

在这里插入图片描述

  • 缩容
    假设将节点3下线,原来路由到3的客户端请求重新路由到节点4,对于其他客户端没有影响,只是这一小部分受到影响
    请求的迁移量达到了最小,这样的算法对于分布式集群来说非常合适,避免了大量请求转移。

在这里插入图片描述

  • 扩容
    新增节点5之后,原来路由到节点3的部分客户端路由到了节点5上,对于其他客户端没有影响,只是这一小部分受到影响

在这里插入图片描述

代码如下:

// 定义服务器IP
String[] servers = {"192.168.31.121", "192.168.3.21", "192.168.1.11", "12.168.31.121", "192.138.3.21", "192.68.1.11"};
// 定义客户端IP
String[] clients = {"12.16.31.121", "12.18.3.1", "19.16.1.11"};
// 计算服务器的Hash,并放到排序的Map中
SortedMap<Integer,String> hashServerMap = new TreeMap<>();
for (String server : servers) {int hashCode = Math.abs(server.hashCode());hashServerMap.put(hashCode,server);
}
// 求客户端IP的Hash,取出对应的服务器
for (String client : clients) {int clientHash = Math.abs(client.hashCode());SortedMap<Integer, String> tailedMap = hashServerMap.tailMap(clientHash);// 取出Hash环上的第一台服务器Integer firstKey;if (tailedMap.isEmpty()){firstKey = hashServerMap.firstKey();}else {firstKey = tailedMap.firstKey();}System.out.println("客户端IP\t"+ client +"\t被路由到了服务器\t" + hashServerMap.get(firstKey));
}

存在的问题及解决方法

如上所述,每一台服务器负责一段,一致性Hash算法对于节点的增加都只需要重定位环空间的一小部分数据,具有较好的容错性和可扩展性。

  1. 一致性Hash算法在服务节点太少时,容易因节点分部不均匀造成数据倾斜问题。例如只有两台服务器,这两台服务器在环上的分部十分靠近,导致某个节点只负责非常小的一段,大量的请求落在了另外一个节点上,导致数据倾斜。
  2. 为了解决这种方法,一致性Hash算法引入了虚拟节点机制,即对每一个服务节点计算多个Hash,每个计算结果都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器IP或者主机名后增加编号来实现,例如 “节点1的ip#1” “节点1的ip#2” “节点1的ip#3”……形成多个虚拟节点,当客户端被路由到虚拟节点的时候其实是被路由到该虚拟节点所对应的真实节点。

代码如下:

// 定义服务器IP
String[] servers = {"192.168.31.121", "192.168.3.21", "192.168.1.11", "12.168.31.121", "192.138.3.21", "192.68.1.11"};
// 定义客户端IP
String[] clients = {"12.16.31.121", "12.18.3.1", "19.16.1.11"};SortedMap<Integer, String> serverHash = new TreeMap<>();int virtualNodeCount = 3;
// 开始计算服务器的Hash
for (String server : servers) {int hash = Math.abs(server.hashCode());serverHash.put(hash, server);// 设置虚拟节点for (int i = 0; i < virtualNodeCount; i++) {int virtualNodeHash = Math.abs((server + "#" + i).hashCode());serverHash.put(virtualNodeHash, "虚拟节点" + i + "映射过来的请求:" + server);}
}System.out.println(serverHash.size());
// 计算客户端请求的服务器Hash
for (String client : clients) {int clientHash = Math.abs(client.hashCode());SortedMap<Integer, String> tailedMap = serverHash.tailMap(clientHash);if (tailedMap.isEmpty()) {Integer firstKey = serverHash.firstKey();System.out.println("客户端:" + client + "\t\t路由到了 \t" + serverHash.get(firstKey));} else {Integer firstKey = tailedMap.firstKey();System.out.println("客户端:" + client + "\t\t路由到了 \t" + serverHash.get(firstKey));}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/45117.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ts实现将相同类型的数据通过排序放在一起

看下效果&#xff0c;可以将相同表名称的字段放在一起 排序适用于中英文、数字 // 排序 function sortByType(items: any) {// 先按照类型进行排序items.sort((a: any, b: any) > {if (a.label < b.label) return -1;if (a.label > b.label) return 1;return 0;});r…

鸿蒙语言基础类库:【@ohos.application.testRunner (TestRunner)】 测试

TestRunner TestRunner模块提供了框架测试的能力。包括准备单元测试环境、运行测试用例。 如果您想实现自己的单元测试框架&#xff0c;您必须继承这个类并覆盖它的所有方法。 说明&#xff1a; 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-…

[氮化镓]Kevin J. Chen组新作—肖特基p-GaN HEMTs正栅ESD机理研究

这篇文章是发表在《IEEE Electron Device Letters》上的一篇关于Schottky型p-GaN栅极高电子迁移率晶体管&#xff08;HEMTs&#xff09;的正向栅极静电放电&#xff08;ESD&#xff09;机理研究的论文。文章由Jiahui Sun等人撰写&#xff0c;使用了基于碳化硅&#xff08;SiC&a…

8626 原子量计数

分析&#xff1a; 1. **读取输入**&#xff1a;首先&#xff0c;我们需要读取输入中的第一行&#xff0c;了解有多少个化学式需要处理。之后&#xff0c;对于每个化学式&#xff0c;我们逐行读取并进行处理。 2. **解析化学式**&#xff1a;对于每个化学式&#xff0c;我们需要…

8627 数独

为了判断数独解是否合法&#xff0c;我们需要遵循以下步骤&#xff1a; 1. **检查每一行**&#xff1a;确保1到9每个数字在每一行中只出现一次。 2. **检查每一列**&#xff1a;确保1到9每个数字在每一列中只出现一次。 3. **检查每个3x3的宫**&#xff1a;确保1到9每个数字在…

【视频】R语言广义加性模型GAMs非线性效应、比较分析草种耐寒性实验数据可视化

全文链接&#xff1a;https://tecdat.cn/?p36979 原文出处&#xff1a;拓端数据部落公众号 广义加法模型&#xff08;Generalized Additive Models, GAMs&#xff09;作为一种高度灵活的统计工具&#xff0c;显著扩展了广义线性模型&#xff08;Generalized Linear Models, …

数字信号处理及MATLAB仿真(4)——量化的其他概念

上回书说到AD转换的两个步骤——量化与采样两个步骤。现在更加深入的去了解以下对应的概念。学无止境&#xff0c;要不断地努力才有好的收获。万丈高楼平地起&#xff0c;唯有打好基础&#xff0c;才能踏实前行。 不说了&#xff0c;今天咱们继续说说这两个步骤&#xff0c;首先…

每日刷题(二分图,二分查找,dfs搜索)

目录 1.P3853 [TJOI2007] 路标设置 2.P1129 [ZJOI2007] 矩阵游戏 3.P1330 封锁阳光大学 4.Trees 5.P1141 01迷宫 1.P3853 [TJOI2007] 路标设置 P3853 [TJOI2007] 路标设置 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 先求出每个路标之间的距离&#xff0c;再二分查找每…

数据库系统原理练习 | 作业2-第2章关系数据库(附答案)

整理自博主本科《数据库系统原理》专业课完成的课后作业&#xff0c;以便各位学习数据库系统概论的小伙伴们参考、学习。 *文中若存在书写不合理的地方&#xff0c;欢迎各位斧正。 专业课本&#xff1a; 目录 一、选择题 二、填空题 三、简答题 四、关系代数 1.课本p70页&…

虚拟机安装Linux CENTOS 07 部署NET8 踩坑大全

首先下载centos07镜像&#xff0c;建议使用阿里云推荐的地址&#xff1a; https://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/?spma2c6h.25603864.0.0.59b5f5ad5Nfr0X 其实这里就已经出现第一个坑了 centos 07 /usr/lib64/ 的 libstdc.so只支持到19&#xff1b; GLI…

数据湖表格式 Hudi/Iceberg/DeltaLake/Paimon TPCDS 性能对比(Spark 引擎)

当前&#xff0c;业界流行的集中数据湖表格式 Hudi/Iceberg/DeltaLake&#xff0c;和最近出现并且在国内比较火的 Paimon。我们现在看到的很多是针对流处理场景的读写性能测试&#xff0c;那么本篇文章我们将回归到大数据最基础的场景&#xff0c;对海量数据的批处理查询。本文…

静态时序分析:Leaf Cell(叶单元)

相关阅读​​​​​​​静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html 在DC中&#xff0c;leaf cell&#xff08;叶单元&#xff09;有时会出现在描述中&#xff0c;例如set_input_delay的-reference_pin选项的参数&#xff0c;就必须是一个端口或…

《昇思25天学习打卡营第18天|onereal》

RNN实现情感分类 概述 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&#xff0c;实现如下的效果&#xff1a; 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This film…

AI版Siri要明年见,研究表明ChatGPT暂无法取代程序员,Kimi推出浏览器插件

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 根据彭博社记者马克古尔曼的最新消息&#xff0c;苹果公司今年不会推出全新的Apple Intelligence驱动的Siri&#xff0c;该公司计划在明年1月开始测试&#xff0c;并在iOS 18.4中才推出正式版本。 此前…

景联文科技以高质量多模态数据集赋能AI大模型,精准匹配提升模型性能

在人工智能的浪潮中&#xff0c;语料数据如同建筑的基石&#xff0c;其质量、规模和运用策略直接决定了AI模型的表现和应用的广泛性。 景联文科技在AI领域深耕多年&#xff0c;打磨了高质量多模态数据集&#xff0c;致力于为不同训练阶段的算法精准匹配高质量数据资源。 3000万…

STM32中断(NVIC和EXIT)

CM3 内核支持 256 个中断&#xff0c;其中包含了 16 个内核中断和 240个外部中断&#xff0c;并且具有 256 级的可编程中断设置。但STM32 并没有使用CM3内核的全部东西&#xff0c;而是只用了它的一部分。STM32有 76 个中断&#xff0c;包括16 个内核中断和 60 个可屏蔽中断&am…

Dify中的RAG和知识库

一.RAG 基本架构 当用户提问 “美国总统是谁&#xff1f;” 时&#xff0c;系统并不是将问题直接交给大模型来回答&#xff0c;而是先将用户问题在知识库中进行向量搜索&#xff0c;通过语义相似度匹配的方式查询到相关的内容&#xff08;拜登是美国现任第46届总统…&#xff0…

深入剖析C++的 “属性“(Attribute specifier sequence)

引言 在阅读开源项目源代码是&#xff0c;发现了一个有趣且特殊的C特性&#xff1a;属性。 属性&#xff08;attribute specifier sequences&#xff09;是在C11标准引入的。在C11之前&#xff0c;编译器特有的扩展被广泛用来提供额外的代码信息。例如&#xff0c;GNU编译器&…

Qt/C++项目积累: 2.主机监控器 - 2.2 历史功能实现

修订历史&#xff1a; 20240711&#xff1a;初始表设计&#xff0c;采用sqlite 正文&#xff1a; 关于历史数据存储&#xff0c;考虑的是用数据库来完成&#xff0c;目前考虑使用Sqlite和mysql&#xff0c;先用sqlite来实现&#xff0c;设计表过程如下&#xff1a; 机器总览…

全网最适合入门的面向对象编程教程:14 类和对象的 Python 实现-类的静态方法和类方法,你分得清吗?

全网最适合入门的面向对象编程教程&#xff1a;14 类和对象的 Python 实现-类的静态方法和类方法&#xff0c;你分得清吗&#xff1f; 摘要&#xff1a; 本文主要介绍了Python中类和对象中的类方法和静态方法&#xff0c;以及类方法和静态方法的定义、特点、应用场景和使用方…