7.8.6 会话保持与用户状态处理
在Nginx部署Web应用时,会话保持决定用户状态在多后端场景下是否一致。常见状态存储方式包括:服务器内存会话、集中式会话、无状态令牌(JWT)。不同策略对应不同的Nginx配置与后端架构,选型不当会导致登录失效、购物车丢失或灰度发布异常。
原理草图(会话保持与状态存储)
会话保持方式与适用场景(含示例)
- 基于源地址的粘滞(ip_hash):适用于内网或IP稳定场景,NAT/移动网络可能失效。
- 一致性哈希:适用于大规模集群,扩缩容时减少会话迁移。
- 基于Cookie的粘滞:适合互联网用户,通常需Nginx Plus或第三方模块。
- 无状态会话(JWT):减少粘滞依赖,需完善密钥、过期与撤销机制。
Nginx配置示例(可直接运行)
1) IP粘滞(内网适用)
# /etc/nginx/conf.d/app.conf
upstream app {
ip_hash;
server 10.0.0.11:8080;
server 10.0.0.12:8080;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
2) 一致性哈希(减少会话迁移)
# /etc/nginx/conf.d/app.conf
upstream app {
hash $request_uri consistent;
server 10.0.0.11:8080;
server 10.0.0.12:8080;
}
3) Cookie粘滞(需要模块/商业版,示例为Nginx Plus语法)
# /etc/nginx/conf.d/app.conf
upstream app {
zone app 64k;
sticky cookie srv_id expires=1h path=/;
server 10.0.0.11:8080;
server 10.0.0.12:8080;
}
说明:开源版需编译第三方模块(如nginx-sticky-module),上线前务必验证兼容性。
安装/编译示例(第三方粘滞模块)
# 1) 获取源码
cd /usr/local/src
wget http://nginx.org/download/nginx-1.24.0.tar.gz
git clone https://github.com/ngx-modules/nginx-sticky-module-ng.git
# 2) 编译安装(保留原参数)
tar xf nginx-1.24.0.tar.gz
cd nginx-1.24.0
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--add-module=/usr/local/src/nginx-sticky-module-ng
make -j4 && make install
# 3) 检查版本与模块
/usr/local/nginx/sbin/nginx -V
集中式会话存储(Redis)示例
- 应用将Session存入Redis,Nginx可使用普通轮询,不强依赖粘滞。
# Redis安装与启动(示例)
yum install -y redis
systemctl enable --now redis
redis-cli ping # 预期输出: PONG
- 预期效果:多后端共享会话,缩容扩容不影响登录态。
JWT无状态方案示例
- 登录后返回JWT,Nginx无需粘滞。
# 客户端示例:保存JWT并访问
curl -X POST http://app.example.com/login -d "u=tom&p=123" -i
# 返回头中包含: Authorization: Bearer <token>
curl -H "Authorization: Bearer <token>" http://app.example.com/api/profile
- 注意:需要令牌过期、刷新机制与撤销列表(黑名单)。
验证与排错(明确命令与预期)
1) 验证是否落到同一后端
# 反复请求查看响应头中的后端标识(需后端返回X-Backend)
for i in {1..5}; do
curl -s -I http://app.example.com/ | grep X-Backend
done
# 预期:粘滞开启时多次请求返回同一后端
2) 检查Set-Cookie是否被丢弃
curl -I http://app.example.com/ | grep -i set-cookie
# 预期:应返回会话Cookie或粘滞Cookie
3) 追踪访问日志定位粘滞失效
# /etc/nginx/nginx.conf 中增加上游标识日志
log_format main '$remote_addr $request $upstream_addr $status';
tail -f /var/log/nginx/access.log
# 观察:同一客户端是否频繁切换upstream_addr
4) 检查会话TTL一致性
# Redis中查看session TTL(示例key)
redis-cli TTL session:uid:1001
# 预期:TTL与应用超时一致,避免“看似登录却失效”
灰度/蓝绿发布的会话建议
- 灰度:用Cookie标记路由,确保用户持续访问同一版本。
- 蓝绿:切换前进行会话迁移或保持兼容读取逻辑。
- 负载均衡器重启:避免依赖本地内存会话,采用集中式存储。
练习
1) 使用ip_hash部署两台后端,验证同一客户端是否命中同一后端。
2) 将会话存储改为Redis,关闭ip_hash,验证登录态仍然稳定。
3) 模拟扩容:新增一台后端,比较一致性哈希与轮询的会话漂移情况。