Nginx 实现会话保持(Session Persistence)主要有 3 种方式 ,生产环境最常用的是 sticky cookie 和 ip_hash,而 sticky learn 是更灵活的高级方案。
一、三种方式对比(先看结论)
| 方式 | 原理 | 优点 | 缺点 | 生产推荐度 |
|---|---|---|---|---|
| ip_hash | 按客户端 IP 哈希分配 | 简单,无需配置 | 代理 IP 会失真,后端宕机会丢会话 | ⭐⭐ 简单场景 |
| sticky cookie | Nginx 插入 cookie,客户端携带 | 精准,不受 IP 影响 | 需要浏览器支持 cookie | ⭐⭐⭐⭐⭐ 首选 |
| sticky learn | 动态学习请求与后端映射 | 兼容性好,不修改响应 | 内存消耗,仅 Nginx Plus 或第三方模块 | ⭐⭐⭐⭐ 大并发场景 |
二、方式一:ip_hash(最简单)
配置示例
nginx
upstream tomcat_cluster {
ip_hash; # 开启 IP 哈希会话保持
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
工作原理
- Nginx 对客户端 IP 地址计算哈希值
- 同一 IP 的请求始终落在同一台后端服务器上
适用场景
- 客户端 IP 相对固定(如企业内网)
- 对会话保持要求不苛刻的场景
典型问题
- 用户通过公司统一出口上网 → 所有用户看起来像一个 IP → 全部落到同一台服务器上
- 后端服务器宕机 → 该服务器上的会话全部丢失
三、方式二:sticky cookie(生产首选,需要编译第三方模块)
Nginx 官方开源版原生不支持,需要编译
nginx-sticky-module-ng模块Nginx Plus 商业版原生支持
配置示例
nginx
upstream tomcat_cluster {
sticky cookie srv_id expires=1h domain=.example.com path=/;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
参数说明
| 参数 | 说明 |
|---|---|
cookie srv_id |
设置 cookie 名称(可自定义) |
expires=1h |
cookie 有效期 1 小时 |
domain=.example.com |
cookie 生效的域名 |
path=/ |
cookie 生效的路径 |
工作原理(关键)
Tomcat2 Tomcat1 Nginx 用户 Tomcat2 Tomcat1 Nginx 用户 1. 首次请求(无 cookie) 2. 选择一台后端(如 tomcat1) 3. 响应内容 4. 响应 + Set-Cookie: srv_id=tomcat1 5. 后续请求(携带 cookie) 6. 根据 cookie 直接发往 tomcat1
优势
- 精准:不受代理 IP 影响
- 透明:后端 Tomcat 无需任何改造
- 兼容性好 :支持
sticky+fallback做高可用
四、方式三:sticky learn(Nginx Plus 或 OpenResty)
配置示例(Nginx Plus)
nginx
upstream tomcat_cluster {
zone upstream_zone 64k;
sticky learn
create=$upstream_cookie_sessionid
lookup=$cookie_sessionid
zone=client_sessions:1m;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
与传统 sticky cookie 的区别
| 对比维度 | sticky cookie | sticky learn |
|---|---|---|
| 谁插入 cookie | Nginx 插入新 cookie | 不插入 cookie,学习应用已有的 cookie |
| 后端应用要求 | 无要求 | 应用需要自己生成一个 sessionid cookie |
| 升级兼容性 | 已有 cookie 可能变化 | 无感知,应用自己的 cookie 不变 |
| 典型使用场景 | 新项目,无历史包袱 | 老项目改造、不想改现有 cookie 逻辑 |
五、三种方式选型决策树
是
否
可容忍
不可容忍
是
否
需要会话保持
客户端 IP 是否相对固定?
会话丢失容忍度?
使用 sticky cookie
ip_hash
不想改造后端应用?
sticky cookie 或 ip_hash
sticky learn
学习应用已有 cookie
六、生产环境最佳实践
推荐方案:sticky cookie + 共享存储兜底
nginx
upstream tomcat_cluster {
sticky cookie srv_id expires=1h;
server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
}
为什么还要做共享存储?
即使 Nginx 做了会话保持,如果后端 Tomcat 宕机,用户会话还是会丢失。
所以 真正的生产高可用 = Nginx 会话保持 + Redis 共享存储:
- Nginx 保证同一用户的请求尽量发到同一台服务器(性能好、减少跨节点读取 session)
- Redis 保证即使那台服务器宕机,其他服务器也能从 Redis 中读取 session
七、排查建议
如果配置了会话保持但还是不生效:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 请求总是落到不同服务器 | cookie 未携带 | 打开浏览器开发者工具 → Application → Cookies,检查 cookie 是否存在 |
| ip_hash 不生效 | 多级代理(X-Forwarded-For) | 使用 real_ip_header 获取真实 IP |
| 负载严重不均 | ip_hash 算法分布不均 | 考虑改用 sticky cookie |
八、一句话总结
Nginx 实现会话保持最常用的方式是通过
sticky cookie给客户端打标记,让后续请求始终落到同一台后端服务器上。但别忘了:Nginx 会话保持 + Redis 共享存储 = 真正的生产高可用。