Skip to content

把单体项目进行了多机部署,多台服务器是如何共享用户登录信息的? ​

  • 标签:后端、场景题

🏛 业务架构师 ​

单体多机部署后 session 共享问题,本质是"登录态从进程内存挪到哪"的问题。我按两条主流路线讲,重点讲各自的业务代价——这也是我实际选型时权衡的点。

路线一:Session 集中存储(改动最小)

  • Spring Session + Redis:登录态写 Redis,请求带 sessionId,任意实例都能取
  • 优点:老系统改造小,session 语义完整(可枚举、可踢人、可过期管理)
  • 代价:每次请求一次 Redis 查询(可用本地缓存优化);Redis 成为登录链路的强依赖,要保证它的高可用

路线二:Token 化(JWT,无状态,主流)

  • 登录后签发 JWT(签名保证不可篡改),客户端每次请求携带,任意实例本地验签即可,不查存储——天然水平扩展、跨服务、跨端
  • 代价必须讲透(面试官一定追问):
    1. 无法主动失效:签发后到期前始终有效,改密码、封号、踢下线都"够不着"已签发的 token
    2. 无法立刻改权限:角色变更要等 token 换新

路线三:混合(生产实践,我的推荐)

短效 access token(JWT)+ Redis 黑名单/版本号:

  • access token 有效期短(2 小时),日常请求纯本地验签(快)
  • 登出/封号/改密时把该用户 token 的版本号写 Redis,网关校验版本(一次 Redis 读,但只在网关做一次)
  • 配 refresh token 静默续期,平衡安全和体验

架构师收尾:登录态方案是安全(可控性)和性能(无状态)的权衡,没有免费午餐。我的经验:C 端产品用"短 JWT + 网关统一校验 + 版本机制",管理后台用集中 session(安全可控优先,性能无所谓)——按业务风险选方案,而不是按技术流行度。

🏗 系统架构师 ​

单机 Session 到多机共享,我按演进讲,核心是把会话状态从应用进程里抽出来。

1. Session 复制(淘汰方案) Tomcat 集群广播同步 session,每次写都要复制到所有节点,节点数一多网络与内存开销指数上升,5 台以上就不可用。只作为反面教材讲。

2. 集中式存储(主流方案) Session 集中存 Redis:Spring 的 spring-session-data-redis 一行配置,用 RedisSessionRepository 替换容器原生 session,应用无代码侵入。多机读写同一份,天然支持扩缩容与发布重启不掉线。要点:

  • Session key 带 userId 前缀便于分析,TTL 滑动续期
  • 读写频率高,注意 Redis 高可用(挂了等于全站掉线),大流量下可用本地缓存 + 版本号做 L1 减压

3. 无状态 Token(现在的默认选择) JWT 把登录态编码进 Token 自包含,服务端不存 session,天然多机/跨域。但 JWT 注销难(有效期内无法作废),所以我的生产实践是混合方案:JWT 只装 userId 与过期时间的签名壳,权限等可变数据查 Redis(或本地缓存),需要踢人时在 Redis 加黑名单,兼顾无状态扩展性与可控性。

4. 粘性会话(补充) LB 按 userId/IP hash 路由固定节点,配合本地 session。缺点是节点挂了 session 丢、扩容要 rehash,只适合过渡。

答题主线:会话外置(Redis)或去状态化(JWT),两者结合是 2026 年的主流答案;同时点出纯 JWT 的注销/续期缺陷与我的取舍。

💻 全栈开发专家 ​

单体多机部署后 Session 不共享,登录态就散在各机器上。两条主流路线:

路线一:Session 集中存 Redis(Spring Session,改动最小)

xml
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
yaml
spring:
  session:
    store-type: redis
    timeout: 30m
  redis:
    host: ...

原理:SessionRepositoryFilter 把 HttpSession 包装为 HttpSessionWrapper,所有读写转发到 Redis(key: spring:session:sessions:{id},hash 结构存属性),Cookie 只携带 SESSION ID。任意一台机器都能读到同一份会话,天然支持水平扩容和踢人(删 key 即可)。

路线二:JWT 无状态

登录签发 JWT(签名自包含用户信息),各机器本地验签即可,不依赖共享存储。代价:无法主动失效(要配黑名单/版本号,等于又回到 Redis),token 里的信息要放过期时间和最低必要 claims。

我的选择 管理后台、需要强制下线/单点登录的平台 → Spring Session + Redis;纯开放 API、无失效诉求 → JWT。 踩过的坑:网关 Nginx 做了 ip_hash 粘性会话掩盖过问题,扩容切轮询后全部掉线——粘性会话只是延缓,不是解决;另外 Session 里别放大对象(序列化开销大),大对象放 Redis 独立 key,Session 只存引用。