Session 还在靠“粘滞”苟活?——Spring Session + Redis 让你从“运气好”升级到“可控”!

举报
bug菌 发表于 2026/01/13 12:02:18 2026/01/13
【摘要】 🏆本文收录于《滚雪球学SpringBoot 3》:https://blog.csdn.net/weixin_43970743/category_12795608.html,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。  本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。...

🏆本文收录于《滚雪球学SpringBoot 3》:https://blog.csdn.net/weixin_43970743/category_12795608.html,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。
  
本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】https://blog.csdn.net/weixin_43970743/article/details/151115907,你想学习的都被收集在内,快速投入学习!!两不误。
  
若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》:https://blog.csdn.net/weixin_43970743/category_11599389.html,涵盖SpringBoot所有版本教学文章。

演示环境说明:

  • 开发工具:IDEA 2021.3
  • JDK版本: JDK 17(推荐使用 JDK 17 或更高版本,因为 Spring Boot 3.x 系列要求 Java 17,Spring Boot 3.5.4 基于 Spring Framework 6.x 和 Jakarta EE 9,它们都要求至少 JDK 17。)
  • Spring Boot版本:3.5.4(于25年7月24日发布)
  • Maven版本:3.8.2 (或更高)
  • Gradle:(如果使用 Gradle 构建工具的话):推荐使用 Gradle 7.5 或更高版本,确保与 JDK 17 兼容。
  • 操作系统:Windows 11

前言:一台机器没事,两台机器就开始“闹脾气”了🙃

Web 应用刚起步时,HttpSession 简直像“温柔乡”:登录态、购物车、验证码,一股脑塞进去,爽得很。
  可一旦你上了负载均衡,或者搞了多个实例,剧情马上变得很现实:

  • 用户 A 第一次请求落到实例 1,Session 在实例 1 的内存里;
  • 下一次请求被打到实例 2 —— Session 没了
  • 然后你开始怀疑人生:“我明明没动代码啊?”😵‍💫

这就是分布式会话管理要解决的核心问题:Session 在哪里、怎么共享、如何隔离、序列化怎么选、Cookie 怎么控
Spring Session 的目标也很明确:提供一套 API 与实现,让你在不绑定容器实现的前提下,支持集群化 Session。

1. Session 粘滞 vs Session 共享:到底谁在“治本”?🤔

1.1 Session 粘滞(Sticky Session / Affinity):看起来省事,实际上很“脆”

粘滞会话的思路是:负载均衡器尽量把同一个用户的请求总是转发到同一台实例上。
优点很直观:

  • 不需要额外存储,继续用内存 Session
  • 改动小,上线快

但它的问题也很扎心:

  • 容灾差:实例挂了,用户 Session 跟着陪葬
  • 扩缩容抖动:实例增减会打破映射,用户被迫重新登录
  • 流量不均:热点用户容易把某台实例压爆
  • 跨协议/跨入口复杂:比如 Web + App + 网关多层转发,粘滞策略越来越像“补丁叠补丁”😅

1.2 Session 共享:把状态放到“大家都能拿到”的地方

Session 共享的思路是:把 Session 数据放到外部存储(Redis/JDBC 等),所有实例读写同一份 Session。
这就是 Spring Session 最常见的落地方式:Redis 存 Session

Spring Session 的 Redis 配置文档也强调:会话数据存储在 Redis 中,并且提供不同实现(如 RedisSessionRepository / RedisIndexedSessionRepository)以适应不同查询需求。

2. 集成 Redis 存储 Session:Spring Boot + Spring Session 的“标准姿势”✅

下面按“能直接抄走跑起来”的节奏写(我知道你要的是这个😄)。

2.1 依赖(Maven 示例)

常见组合是:

  • spring-session-data-redis
  • spring-boot-starter-data-redis

(具体版本跟你的 Spring Boot BOM 对齐即可)

2.2 配置(application.yml)

spring:
  session:
    # Servlet 项目用 Spring Session 管理 HttpSession
    # 具体 store 类型由依赖与自动配置决定;这里给个明确意图
    timeout: 30m
  data:
    redis:
      host: 127.0.0.1
      port: 6379

关于超时:Spring Boot 文档明确指出可以用 spring.session.timeout 设置 Session 超时时间;若不设置,会回退到 server.servlet.session.timeout

2.3 需要显式 @EnableRedisHttpSession 吗?

  • Spring Boot 场景:多数情况下依赖齐全会走自动配置,你可以先不写注解。
  • 你要更细控制(比如 flushMode/saveMode/namespace 等):可以使用 @EnableRedisHttpSession,它的配置语义(如 ON_SAVE 等)在 API 文档里有说明。

3. 自定义 Session 序列化策略:JSON vs JDK Serialization(别只看“可读性”,也要看“坑位”😅)

3.1 默认是什么?

Spring Session Redis 默认使用 Java Serialization(JDK 序列化) 来序列化 Session 属性。官方文档点得很直白:默认 Java 序列化在多应用共享 Redis、类版本不一致时可能会出问题。

JDK 序列化的典型痛点

  • Redis 里是二进制,不可读(排查不友好)
  • 需要 Serializable,对象一变更容易兼容性炸裂
  • 多服务共享同一 Redis、但类版本不一致时,反序列化风险更高

3.2 JSON 序列化(推荐更稳的“跨服务/跨版本”体验)

Spring Session 文档明确:你可以提供一个 RedisSerializer bean 来自定义会话如何序列化到 Redis;并点名 Spring Data Redis 的 GenericJackson2JsonRedisSerializer 可用。

做法:提供 springSessionDefaultRedisSerializer Bean

(这个名字很关键:Spring Session 会按约定去找默认序列化器)

@Configuration
public class SessionSerializationConfig {

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        // 使用 Jackson 的通用 JSON 序列化,包含类型信息,兼容性更强
        return new GenericJackson2JsonRedisSerializer();
    }
}

JSON 的注意点(别装没看见🙈)

  • JSON 序列化会把类型信息写进去(GenericJackson2JsonRedisSerializer 会处理这点),否则反序列化会丢类型
  • Session 里如果塞了“奇怪对象”(比如带懒加载代理的实体、巨大对象图),不管 JSON 还是 JDK 都会疼
  • 建议:Session 里只放轻量、稳定、可序列化的 DTO/标识(比如 userId、role、少量权限信息)

4. 同域名下多应用 Session 隔离与共享:CookieSerializer 才是“分隔线”🍪

这里是最容易“看起来都配置了,但就是不对”的地方。因为你以为你在搞 Redis,其实你在搞 Cookie😅

Spring Session 的“自定义 Cookie”指南明确:你可以暴露 CookieSerializer Bean 来配置 Cookie 行为,Spring Session 自带 DefaultCookieSerializer
DefaultCookieSerializer 的 API 里也清楚列出了 cookieNamepathdomainsameSite 等控制项(默认 SameSite=Lax)。

4.1 先说结论:共享与隔离,本质看两件事

✅ 想“共享 Session”(同域名不同应用互通登录态)

你需要让这些应用:

  1. 写入同一个 Session ID Cookie(cookieName/domain/path 一致)
  2. 访问同一份 Redis Session 数据(同一 Redis + 同一 namespace)

✅ 想“隔离 Session”(互不影响)

你可以在任意一个维度做隔离:

  • Cookie 隔离:不同 cookieName 或不同 cookiePath
  • Redis 隔离:不同 spring.session.redis.namespace(推荐)

Spring Session Redis 配置文档明确提到:Spring Session 用 namespace(默认 spring:session)来隔离不同应用的会话数据,并可通过 spring.session.redis.namespace 指定。

4.2 实战:同域名多应用“隔离”(最常见,最安全)

假设你有:

  • app-a.example.com
  • app-b.example.com
    你希望它们登录态互不影响。

方案 A:Redis namespace 隔离(强烈推荐)

app-a

spring:
  session:
    redis:
      namespace: app-a:session

app-b

spring:
  session:
    redis:
      namespace: app-b:session

这样即使 cookieName 一样,Redis key 也隔开了(更稳)。

方案 B:CookieName 隔离(也常用)

@Configuration
public class SessionCookieConfig {

    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("SESSION_APP_A");
        serializer.setCookiePath("/");
        return serializer;
    }
}

DefaultCookieSerializer 支持设置 Cookie 名称、Path、Domain、SameSite 等。

4.3 实战:同主域名下多应用“共享”(单点登录味道😄)

场景:

  • a.example.comb.example.com 希望共享 Session(同一登录态)
    关键是把 Cookie 的 domain 设置成主域 example.com,并确保 cookiePath 通常为 /
@Configuration
public class SharedSessionCookieConfig {

    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("SESSION");
        serializer.setCookiePath("/");
        serializer.setDomainName("example.com");  // 让子域共享
        serializer.setSameSite("Lax");            // 默认就是 Lax,可按需改
        return serializer;
    }
}

DefaultCookieSerializer#setSameSite 的默认值是 Lax(API 文档注明)。

小提醒:如果你在本地用 localhost 测子域共享,domain 规则会比较别扭;建议用 hosts 文件映射一个测试域名来验证(不然你会怀疑配置没生效,实际是浏览器 cookie 规则在“守法”😂)。

5. 你会在生产环境踩的几个“高频雷区”💥(提前说清楚,省得你线上背锅)

5.1 Session 里放了“巨大对象/不可序列化对象”

  • JDK 序列化会直接炸或存储膨胀
  • JSON 序列化可能循环引用、类型丢失、性能抖动
    建议:Session 只放稳定的小对象(id/少量字段)。

5.2 多应用共享 Redis 却没隔离 namespace

结果:

  • A 应用写的 Session,B 应用误读、或因类版本不同反序列化失败
    Spring Session 文档已经点过这个风险,并提供 namespace 作为隔离手段。

5.3 Cookie 域/路径没配对

  • 你以为共享,实际上 cookie 发不到另一个应用
  • 你以为隔离,实际上 cookieName 一样 + path 一样,浏览器照样带过去
    DefaultCookieSerializer 这类配置要和你的域名/部署路径强绑定思考。

结尾:你要的不是“能跑”,是“可预期”🙂

说到底,分布式 Session 管理就是把“用户状态”从单机内存搬到一个更可靠的地方,并把 Cookie/序列化/命名空间这些边界划清楚。
粘滞会话能救急,但救不了长期;Spring Session + Redis 才是把系统从“看缘分”拉回“可控”的方案。

🧧福利赠与你🧧

  无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。

最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。

同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G PDF编程电子书、简历模板、技术文章Markdown文档等海量资料。

ps:本文涉及所有源代码,均已上传至Gitee:https://gitee.com/bugjun01/SpringBoot-demo 开源,供同学们一对一参考 Gitee传送门https://gitee.com/bugjun01/SpringBoot-demo,同时,原创开源不易,欢迎给个star🌟,想体验下被🌟的感jio,非常感谢❗

🫵 Who am I?

我是 bug菌:

  • 热活跃于 CSDN:https://blog.csdn.net/weixin_43970743 | 掘金:https://juejin.cn/user/695333581765240 | InfoQ:https://www.infoq.cn/profile/4F581734D60B28/publish | 51CTO:https://blog.51cto.com/u_15700751 | 华为云:https://bbs.huaweicloud.com/community/usersnew/id_1582617489455371 | 阿里云:https://developer.aliyun.com/profile/uolxikq5k3gke | 腾讯云:https://cloud.tencent.com/developer/user/10216480/articles 等技术社区;
  • CSDN 博客之星 Top30、华为云多年度十佳博主&卓越贡献奖、掘金多年度人气作者 Top40;
  • 掘金、InfoQ、51CTO 等平台签约及优质作者;
  • 全网粉丝累计 30w+

更多高质量技术内容及成长资料,可查看这个合集入口 👉 点击查看:https://bbs.csdn.net/topics/612438251 👈️
硬核技术公众号 「猿圈奇妙屋」https://bbs.csdn.net/topics/612438251 期待你的加入,一起进阶、一起打怪升级。

- End -

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。