一、引言:无状态的 http 与有状态的业务
http 协议本身是无状态的。这意味着,服务器处理完一个请求后,不会保留任何关于客户端的信息。然而,现实中的绝大多数 web 应用(如电商、社交、后台管理系统)都是有状态的——我们需要知道“你是谁”、“你购物车里有什么”。
这个矛盾催生了 session (会话) 机制。但当我们的应用从单机走向集群,由 nginx 进行负载均衡时,一个棘手的问题出现了:session 一致性(session stickiness / session persistence)。
💡 核心问题:
用户的第一次请求被分配到 server a 并创建了 session,第二次请求却被 nginx 分配到了没有该 session 的 server b,导致用户“掉线”!
本文将为你揭示解决这一难题的三大主流方案,并分析其优劣。
二、方案一:nginx 粘性会话(ip hash / sticky cookie)
这是最简单、最直接的方案,思路是让同一个用户的请求始终被路由到同一台后端服务器。
1. ip hash(基于客户端 ip)
nginx 的 ip_hash 指令会对客户端的 ip 地址进行哈希计算,确保来自同一 ip 的请求总是落到同一个后端节点。
upstream backend {
ip_hash; # 启用ip哈希
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}优点:
- 配置极其简单。
- 不需要修改应用代码。
致命缺点:
- 代理/cdn 穿透问题:如果用户通过 nat、公司代理或 cdn 访问,nginx 看到的将是代理服务器的 ip,而非真实用户 ip。这会导致大量不同用户被错误地路由到同一台服务器,造成严重的负载不均。
- 服务器扩缩容困难:增加或减少后端服务器会改变哈希环,导致大部分用户的会话失效。
2. sticky cookie(基于 cookie)
nginx plus(商业版)提供了更优雅的 sticky cookie 指令。它会在用户首次访问时,在响应中植入一个特殊的 cookie(如 route=server01),后续请求携带此 cookie,nginx 就能据此进行路由。
# nginx plus 配置示例
upstream backend {
sticky cookie srv_id expires=1h domain=.example.com path=/;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}优点:
- 精准度高,不受代理影响。
- 用户体验好。
缺点:
- 仅限 nginx plus:开源版 nginx 不支持。
- 仍然无法解决扩缩容问题:服务器列表变化,cookie 中的标识可能失效。
总结:粘性会话是一种“治标不治本”的方案,适用于小型、稳定的集群,但在现代弹性化、云原生的架构中已逐渐被淘汰。
三、方案二:集中式 session 存储(推荐方案)
这是业界最主流、最可靠的解决方案。其核心思想是:将会话数据从应用服务器的内存中剥离出来,存放到一个所有服务器都能访问的共享存储中。
架构图
[用户] --> [nginx]
|
v
+-----------------------+
| 应用服务器 |
| - app server 1 | <----+
| - app server 2 | <----+---> [redis cluster]
| - ... | <----+
+-----------------------+实现方式
- 应用层改造:修改你的应用程序(如 java 的 spring session, php 的
session.save_handler),配置其使用 redis 或 memcached 作为 session 存储后端。 - 部署共享存储:搭建一个高可用的 redis 集群或 memcached 集群。
优点:
- 彻底解耦:应用服务器变为完全无状态,可以随意扩缩容、重启、替换,而不会影响用户会话。
- 高可用:redis/memcached 本身可以做主从、集群,保证 session 数据的可靠性。
- 弹性伸缩:完美契合云原生和微服务架构。
缺点:
- 引入新组件:增加了系统复杂度,需要维护 redis/memcached 集群。
- 网络开销:每次请求都需要额外一次网络调用去读取 session,略微增加延迟(通常可忽略)。
总结:虽然初期投入稍大,但集中式存储是构建现代化、可扩展系统的基石,是强烈推荐的生产级方案。
四、方案三:无状态 token(jwt)
这是一种更为激进的架构思想——彻底抛弃服务端 session。
原理
- 用户登录成功后,服务器生成一个 jwt (json web token),其中包含了用户的身份信息(如 user_id, role)和有效期,并用私钥签名。
- 服务器将 jwt 返回给客户端,客户端将其存储在 localstorage 或 cookie 中。
- 客户端后续的每个请求都携带此 jwt(通常在
authorizationheader 中)。 - 服务器收到请求后,只需验证 jwt 的签名和有效期即可确认用户身份,无需查询任何存储。
nginx 的角色
nginx 在这里主要扮演反向代理和静态资源服务器的角色,对 jwt 本身不做特殊处理。验证逻辑完全在后端应用中完成。
优点:
- 极致的可扩展性:服务器完全无状态,水平扩展变得异常简单。
- 跨域友好:非常适合前后端分离、多端(web/ios/android)的应用架构。
- 减少数据库压力:省去了频繁读写 session 存储的开销。
缺点:
- token 无法主动失效:jwt 一旦签发,在过期前一直有效。如果需要实现“强制下线”功能,仍需引入一个 token 黑名单(又回到了存储方案)。
- payload 大小限制:不适合在 token 中存放大量数据。
- 安全要求高:必须使用 https,妥善保管签名密钥。
总结:jwt 是 api 时代和微服务架构下的宠儿,特别适合移动端和 spa(单页应用)。
五、结语
到此这篇关于nginx会话管理的三种主流方案的文章就介绍到这了,更多相关nginx会话管理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论