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) 模拟扩容:新增一台后端,比较一致性哈希与轮询的会话漂移情况。