一、引言:为什么需要 sticky 模块?
在使用 Nginx 作为反向代理进行负载均衡时,一个经典难题是 会话保持(Session Persistence)。用户的请求可能会被分发到不同的后端应用服务器(如 Tomcat, Node.js),而这些服务器之间通常不共享 Session 数据,导致用户频繁"掉线"。
Nginx 自带的 ip_hash 策略虽然能解决部分问题,但它存在致命缺陷:
- 受代理/CDN 影响:Nginx 看到的是代理服务器的 IP,而非真实用户 IP,导致大量用户被错误地路由到同一台服务器,造成严重负载不均。
- 无法应对动态扩缩容。
为了解决这个问题,社区开发了 nginx-sticky-module 这个强大的第三方模块。它通过 植入 Cookie 的方式,精准地将同一用户的请求始终路由到同一台后端服务器,完美实现了会话保持。
💡 核心价值 :
sticky模块提供了一种比ip_hash更精准、更可靠的会话保持机制,是开源 Nginx 实现粘性会话的最佳实践!
二、准备工作:环境与依赖
由于 sticky 是一个第三方模块,我们需要从源码重新编译 Nginx 来集成它。
1. 系统环境
本文以 CentOS 7 / Rocky Linux 8 为例。
2. 安装编译依赖
bash
sudo yum install -y gcc gcc-c++ make libtool pcre pcre-devel openssl openssl-devel zlib zlib-devel
3. 下载 Nginx 源码
请务必下载与你当前生产环境相同版本的 Nginx 源码,以避免兼容性问题。
bash
# 例如,下载 Nginx 1.24.0
cd /usr/local/src
wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar -zxvf nginx-1.24.0.tar.gz
4. 下载 sticky 模块源码
nginx-sticky-module 有多个分支,推荐使用维护较好的 nginx-sticky-module-ng。
bash
cd /usr/local/src
wget https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/get/08a395c66e42.zip
unzip 08a395c66e42.zip
# 解压后会得到一个类似 "nginx-goodies-nginx-sticky-module-ng-08a395c66e42" 的目录
mv nginx-goodies-nginx-sticky-module-ng-08a395c66e42 nginx-sticky-module-ng
三、核心步骤:编译并集成 sticky 模块
1. 查看现有 Nginx 的编译参数(重要!)
为了保证新编译的 Nginx 功能完全一致,我们需要先获取当前 Nginx 的编译参数。
bash
nginx -V
你会看到类似如下的输出:
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx ... --with-http_ssl_module ...
复制 configure arguments 后面的所有内容。
2. 进入 Nginx 源码目录并配置
bash
cd nginx-1.24.0
# 在原有参数的基础上,加上 --add-module 指向 sticky 模块的路径
./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
... # 这里是你之前复制的所有原有参数
--add-module=/usr/local/src/nginx-sticky-module-ng
3. 编译(但不要安装!)
执行 make 命令进行编译。这一步不会覆盖你现有的 Nginx 安装,只会生成一个新的可执行文件。
bash
make
编译成功后,新的 Nginx 可执行文件位于 objs/nginx。
4. 备份并替换原 Nginx 二进制文件
bash
# 备份原文件
sudo cp /usr/sbin/nginx /usr/sbin/nginx.bak
# 替换为新编译的文件
sudo cp objs/nginx /usr/sbin/nginx
5. 验证模块是否加载成功
bash
nginx -V 2>&1 | grep -o with-http_sticky_module
如果输出 with-http_sticky_module,则说明模块已成功集成。
四、配置与实战:启用 sticky 负载均衡
现在,我们可以在 Nginx 配置文件中使用 sticky 指令了。
1. 基本配置示例
编辑你的 nginx.conf 文件,在 upstream 块中添加 sticky 指令。
bash
upstream backend {
# 启用 sticky 模块
# name: Cookie 名称
# expires: Cookie 过期时间
# path: Cookie 作用路径
sticky cookie srv_id expires=1h domain=.yourdomain.com path=/;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name yourdomain.com;
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;
}
}
2. 关键参数详解
name: 客户端 Cookie 的名称。默认为route。expires: Cookie 的过期时间。可以是1h(1小时),30m(30分钟), 或max(永不过期)。domain: Cookie 的作用域。设置为你的主域名(如.yourdomain.com)可以让子域名也共享此 Cookie。path: Cookie 的作用路径。通常设为/。hash=md5|sha1: 用于生成后端服务器标识的哈希算法。默认是md5。
3. 工作原理
- 首次访问 :客户端请求头中没有
srv_idCookie。Nginx 根据负载均衡策略(如轮询)选择一台后端服务器,并在响应头中植入Set-Cookie: srv_id=xxx; ...。 - 后续访问 :客户端在请求头中携带
Cookie: srv_id=xxx。Nginx 解析此 Cookie,直接将请求转发给上次分配的那台服务器。
五、验证与排错
1. 验证会话保持
- 使用浏览器开发者工具,观察首次响应是否包含
Set-Cookie。 - 刷新页面,检查后续请求的
Cookie头是否包含srv_id。 - 在后端应用服务器上打印请求日志,确认同一用户的请求始终落在同一台服务器上。
2. 常见问题与解决
-
问题1:编译报错。
- 原因 :
sticky模块版本与 Nginx 版本不兼容。 - 解决 :尝试寻找与你 Nginx 版本匹配的
sticky模块分支,或修改模块源码中的兼容性代码(通常涉及ngx_http_upstream_srv_conf_t结构体)。
- 原因 :
-
问题2:Cookie 未生效。
- 原因 :客户端禁用了 Cookie,或者
domain/path配置不正确。 - 解决 :检查浏览器设置,并确保
domain配置与你的访问域名匹配。
- 原因 :客户端禁用了 Cookie,或者
-
问题3:后端服务器宕机。
- 注意 :
sticky模块本身不包含健康检查 功能。你需要配合max_fails和fail_timeout参数,或使用外部健康检查脚本,才能自动剔除故障节点。
- 注意 :
六、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!