Nginx | 负载均衡策略:一致性哈希算法实践

知识是人生的灯塔,只有不断学习,才能照亮前行的道路

📢 大家好,我是 WeiyiGeek,一名深耕安全运维开发(SecOpsDev)领域的技术从业者,致力于探索DevOps与安全的融合(DevSecOps),自动化运维工具开发与实践,企业网络安全防护,欢迎各位道友一起学习交流、一起进步 🚀,若此文对你有帮助,一定记得点个关注⭐与小红星❤️或加入到作者知识星球『 全栈工程师修炼指南』,转发收藏学习不迷路 😋 。

负载均衡策略:一致性哈希算法

描述:上一小节《Nginx | 负载均衡策略:ip_hash / hash 会话保持实践》说到,哈希算法在服务器数量变化时导致请求路由失效的问题,为了解决这个问题,一致性哈希算法应运而生。它通过构建一个环形结构来映射服务器和请求的哈希值,从而实现负载均衡的同时保持缓存的一致性。

温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_a2l6pacq0flr.html

好了,本小节作者将介绍一致性哈希算法原理,并实践在 upstream 哈希模块中通过添加 consistent 配置,从而实现该算法。

go 复制代码
# 基于哈希键值的服务器组指定负载均衡算法。
Syntax: hash key [consistent];
Default: ---
Context: upstream

探究哈希算法存在的问题

问题背景:当上游服务器数量发生变化(增加或减少)时,传统的哈希算法会导致大量请求的路由策略失效。

示例说明:假设有5个键
,对应5台服务器
,采用模5哈希算法:

  • → 路由到 server 0

  • → 路由到 server 1

  • → 路由到 server 2

  • → 路由到 server 3

  • → 路由到 server 4

若移除一台服务器,变为4台,则哈希算法改为模4,服务器减少后的变化如下:

  • → 原本路由到 server 0,现变更为 server 1

  • → 原本路由到 server 1,现变更为 server 2

  • → 原本路由到 server 2,现变更为 server 3

  • → 原本路由到 server 3,现变更为 server 0

  • → 原本路由到 server 4,现变更为 server 1

weiyigeek.top-HASH算法问题图

由上图可知,所有5个键的路由目标全部发生改变,尽管只减少了一台服务器,若此时上游服务未共享缓存(通常采用 redis 或 PostgreSQL)时,例如,采用 cookie / session 方式记录用户会话,这将导致大量缓存失效或会话中断。

如何解决哈希算法存在的问题?

不言而喻,就是一致性 Hash 算法,其核心是将服务器节点和键值都映射到一个环形哈希空间上,从而降低服务器变动时对整体路由的影响。

  • 环形空间构建:使用32位无符号整型构成一个环,范围为

  • 节点放置方式:将每台服务器(如 server 0~3)通过哈希函数映射到环上的某个位置,并按顺时针方向分配数据

  • 键的路由规则:每个键
    经哈希后也落在环上,沿顺时针找到第一个遇到的服务器节点,即为其目标节点

例如,将四台服务器均匀分布在环上,每个节点负责从其前一个节点到自身的区间内的所有键;若增加一台服务器则根据负载情况决定插入位置,计算请求HASH键如果正好落于 node5 (server 5) 负责于自身区间中与前一个节点范围,则由其路由转发。

简单来说,新增节点后,只有新增节点周围的键受到影响,其余键的路由保持不变,例如,下图中原5个键均路由至 node 4,其后新增 node 5,通过哈希计算得出其中2个键仍落在 node 4 的管辖范围内,不受影响。另外3个键现在更接近 node 5,因此改由 node 5 处理,所以当未共享缓存时,仅这3个键对应的缓存会失效,不会导致大范围的失效,其余键的缓存继续命中 HITS。
weiyigeek.top-一致性 Hash 算法环形哈希示例图

一致性哈希的优势与劣势

  • 主要优势:扩容或缩容时,仅影响新增/删除节点附近的键,其他键的路由保持不变。极大缓解了因服务器变动导致的大规模缓存失效问题。特别适用于高并发、依赖缓存的系统环境。

  • 主要劣势:无法完全避免路由变化,部分键仍需重新分配。已宕机的节点无法恢复其原有状态,但此非设计目标。基础版本可能存在负载不均问题(未引入虚拟节点时)。

操作步骤

步骤 01.创建两个测试服务器(213、214),213主机监听 8010 端口,214主机分别监听8011和8012端口。

go 复制代码
# 编辑主配置文件,添加 include 指令,引入自定义配置文件目录。
vim /usr/local/nginx/conf/nginx.conf
....
include /usr/local/nginx/conf.d/*.conf;
....

# 创建测试服务器配置文件目录,并写入两个监听服务器的配置。
mkdir /usr/local/nginx/conf.d/
# 10.20.172.214 主机
tee /usr/local/nginx/conf.d/server.conf <<'EOF'
server {
  listen 8011;
  server_name localhost _;
  default_type text/plain;
return 200 'From 8011 server response!\n';
}

server {
  listen 8012;
  server_name localhost _;
  default_type text/plain;
return 200 'From 8012 server response!\n';
}
EOF

# 10.20.172.213 主机
tee /usr/local/nginx/conf.d/server.conf <<'EOF'
server {
  listen 8010;
  server_name localhost _;
  default_type text/plain;
return 200 'From 10.20.172.213:8011 server response!\n';
}
EOF

步骤 02.编辑 Nginx 主配置文件,反向代理请求时使 启用一致性哈希算法。

go 复制代码
mv /usr/local/nginx/conf.d/proxy_server_iphash.conf{,.bak}
tee /usr/local/nginx/conf.d/proxy_server_hash.conf <<'EOF'
# 创建上游服务器
upstream backend {
# 基于指定字符串 hash 的负载平衡方法。
# hash user_$arg_user; # 基于 user_拼接请求url中的user参数进行哈希运算,不受权重影响
hash user_$arg_user consistent; # 基于一致性哈希算法进行负载均衡。
# hash 负载均衡算法不受权重影响
  server 127.0.0.1:8011 weight=2; 
  server 127.0.0.1:8012;
# 最大失败次数为2,暂停10秒不向其转发请求。
  server 10.20.172.213:8010 max_fails=2 fail_timeout=10s max_conns=5;
  keepalive 10;  # 设置与上游服务器的长连接,最多保持10个空闲的保活连接。
  keepalive_timeout 60s; # 设置与上游服务器的长连接,空闲连接的超时时间。
}

# 创建监听用于反向代理
server {
  listen 80;
  server_name test.weiyigeek.top;
  access_log /var/log/nginx/test.log main;
  error_log test.error.log info;
  location / {
    proxy_pass http://backend;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Connection "";
    proxy_http_version 1.1;
  }
}
EOF

步骤 03.重启 Nginx 服务,测试一致性哈希算法效果。

go 复制代码
# 214 主机
nginx -s reload

# 214 主机,模拟客户端请求
for i in $(seq 1 30);do echo -n "user_WeiyiGeek${i}:";curl -H 'X-Forwarded-For: 100.10.2.215' http://test.weiyigeek.top?user=WeiyiGeek${i};sleep 1;done

# 在请求过程中在 213 主机上模拟服务器宕机,并再次测试一致性哈希算法效果。
nginx -s stop

weiyigeek.top-测试一致性哈希算法效果图

END

加入:作者【全栈工程师修炼指南】知识星球

『 全栈工程师修炼指南』星球,主要涉及全栈工程师(Full Stack Development)实践文章,持续更新包括但不限于企业SecDevOps和网络安全等保合规、安全渗透测试、编程开发、云原生(Cloud Native)、物联网工业控制(IOT)、人工智能Ai,从业书籍笔记,人生职场认识等方面资料或文章。

Q: 加入作者【全栈工程师修炼指南】星球后有啥好处?

✅ 将获得作者最新工作学习实践文章以及网盘资源。

✅ 将获得作者珍藏多年的全栈学习笔记(需连续两年及以上老星球友,也可单次购买)

✅ 将获得作者专门答疑学习交流群,解决在工作学习中的问题。

✅ 将获得作者远程支持(在作者能力范围内且合规)。

获取:作者工作学习全栈笔记

作者整理了10年的工作学习笔记(涉及网络、安全、运维、开发),需要学习实践笔记的看友,可添加作者微信或者回复【工作学习实践笔记】,当前价格¥299,除了获得从业笔记的同时还可进行问题答疑以及每月远程技术支持,希望大家多多支持,收获定大于付出!

知识推荐 往期文章

若文章对你有帮助,请将它转发给更多的看友,若有疑问的小伙伴,可在评论区留言你想法哟 💬!

相关推荐
程序员-King.2 小时前
day115—同向双指针—将x减到0的最小操作数(LeetCode-1658)
算法·leetcode·双指针
jianchwa2 小时前
Linux Kernel PCIe SRIOV机制分析
linux·运维·服务器
Jerryhut2 小时前
sklearn函数总结六——特征降维 压缩数据 - 特征提取(PCA&LDA)
人工智能·算法·机器学习·scikit-learn·sklearn
9ilk2 小时前
【Linux】--- 五种IO模型
linux·运维·网络
西岭千秋雪_2 小时前
MySQL日志梳理(服务器层)
java·运维·服务器·数据库·mysql
apcipot_rain2 小时前
CCF算法能力大赛T3 暴力法 反思
算法
前端小白在前进2 小时前
力扣刷题:有效的括号
算法·leetcode·职场和发展
EXtreme352 小时前
算法深潜:链表中的生死之环(LeetCode 141 & 142 详解)
数据结构·算法·leetcode·链表·快慢指针·数学证明·带环链表
0wioiw02 小时前
Docker(基础指令)
运维·docker·容器