分布式Session
我用「餐厅点餐+代码实战」帮你彻底搞懂分布式Session,看完不仅能应对面试,还能直接应用到实际开发。先记住这个核心矛盾:多服务员如何记住同一顾客的喜好?
一、从生活场景理解Session的本质
传统单机场景(小餐馆)
- 服务员:Tom(唯一服务员)
- 工作流程:
- 顾客首次点餐 → Tom给纸质会员卡(SessionID)
- Tom把顾客口味记录在自己的笔记本(服务器内存)
- 顾客下次出示会员卡 → Tom查笔记本提供服务
分布式场景(连锁餐厅)
- 服务员:Tom、Jerry、Lucy(多个服务器节点)
- 致命问题:
- 顾客第一次找Tom存了爱吃辣 → 第二次请求被分配到Jerry → Jerry一脸懵逼
二、分布式Session五大解决方案
方案1:黏性会话(Sticky Session)
- 原理:让同一用户的请求始终路由到同一服务器
- 实现:Nginx配置ip_hash
upstream backend {ip_hash; # 像给顾客发固定服务员工牌server 192.168.1.101:8080;server 192.168.1.102:8080;
}
- 优点:零改造成本
- 缺点:
- 服务器宕机 → Session丢失(相当于服务员请假,笔记本被带走)
- 扩容缩容困难(新服务员没有历史记录)
方案2:Session复制(同步广播)
- 原理:所有服务器实时同步Session数据
- 实现:Tomcat配置集群
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
- 优点:任意服务器都可响应
- 缺点:
- 网络带宽消耗大(相当于每天让所有服务员互相抄笔记)
- 不适合大规模集群(超过10个节点性能暴跌)
方案3:集中存储(重点掌握)
- 原理:把Session存到独立存储服务
- 架构:
用户 → 负载均衡 → 任意服务器 → Redis/Memcached
- 代码示例(Spring Session + Redis):
- 添加依赖:
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId> </dependency>
- 配置Redis连接:
@EnableRedisHttpSession public class Config {@Beanpublic LettuceConnectionFactory connectionFactory() {return new LettuceConnectionFactory("redis-host", 6379);} }
- 使用Session与单机完全一致:
@GetMapping("/login") public String login(HttpSession session) {session.setAttribute("user", "码农阿杜"); // 自动存到Redisreturn "登录成功"; }
- 优点:
- 服务器无状态,方便扩容
- 数据持久化,服务器重启不丢失
- 缺点:
- 增加网络延迟(多一次存储访问)
- 需要维护中间件
方案4:客户端存储(JWT方案)
- 原理:把Session数据加密后直接存Cookie
- 代码示例:
// 生成Token String token = Jwts.builder().setSubject("user123").claim("role", "admin").signWith(SignatureAlgorithm.HS256, "secretKey").compact();// 验证Token Claims claims = Jwts.parser().setSigningKey("secretKey").parseClaimsJws(token).getBody();
- 优点:彻底解决服务端存储问题
- 缺点:
- Token无法主动失效(相当于会员卡永久有效)
- 数据大小受Cookie限制
方案5:Session共享协议(Token+数据库)
- 实现流程:
- 登录成功生成token(UUID)
- 把token和用户数据存入数据库
- 每次请求携带token查询数据库
- 代码示例:
// 生成Token String token = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(token, userInfo, 30, TimeUnit.MINUTES);// 拦截器验证 String token = request.getHeader("X-Token"); User user = redisTemplate.opsForValue().get(token); if(user == null) throw new AuthException();
- 优点:灵活控制存储方式
- 缺点:需要手动管理生命周期
三、方案选型决策树
是否需要服务器完全无状态?
├─ 是 → 客户端存储(JWT)
└─ 否 → 是否需要高并发?├─ 是 → 集中存储(Redis)└─ 否 → Session复制(小集群)/黏性会话(稳定集群)
四、面试高频问题
Q1:如何防止Session劫持?
- 防御措施:
- 使用HTTPS防止网络嗅探
- Cookie设置HttpOnly和Secure
- 定期更换SessionID(如每10分钟)
Q2:分布式Session过期时间如何设置?
- 黄金法则:
- 基础过期时间:30分钟
- 滑动过期:每次访问刷新有效期
// Redis示例 redisTemplate.expire(sessionId, 30, TimeUnit.MINUTES);
Q3:Spring Session的实现原理?
- 核心机制:
- 通过Filter替换原生HttpSession
- SessionRepositoryFilter包装请求/响应
- 实际存储委托给Redis等实现
五、生产环境最佳实践
- Session数据最小化:只存必要信息(如userId)
- 读写分离:Redis主从架构提升读取性能
- 降级方案:在Redis故障时自动切换本地缓存
- 监控预警:监控Session存储容量和延迟
一句话总结
分布式Session的本质是:把服务员们的笔记本换成中央档案室! 掌握这个本质,所有解决方案都是围绕存储位置和同步方式的设计权衡。
补充
方案3和方案5确实都涉及外部存储,但它们的核心差异在于数据管理层次和实现方式。我用餐厅工作流程对比帮你彻底分清这对「双胞胎」:
本质区别对比表
方案3:集中存储(Spring Session) | 方案5:Session共享协议(Token+DB) | |
---|---|---|
管理层次 | Web容器层自动管理(对开发者透明) | 应用层手动管理(需要显式编码) |
存储内容 | 完整Session对象(序列化存储) | 自定义业务数据(如用户ID、权限等) |
标识传递 | 自动通过Cookie传递JSESSIONID | 手动通过Header/Param传递自定义Token |
数据读写 | 框架自动完成(如Spring Session Filter拦截读写) | 需要手动编写存取代码 |
典型应用 | 传统Web应用迁移到分布式环境 | 前后端分离架构/APP接口 |
餐厅版对比解释
假设餐厅要记录顾客的「忌口清单」:
方案3:中央档案室(Spring Session)
- 服务员直接说:“忌口清单存总部”
- 每次顾客出示会员卡 → 服务员自动联系总部查清单
- 优势:服务员工作方式不变,只是数据位置换了
方案5:自定义登记表(Token+DB)
- 服务员需要:
- 设计新的登记表格(定义Token格式)
- 手动打电话给总部:“把顾客A的清单给我”
- 更新后主动回传总部:“这是顾客A的新清单”
- 优势:完全掌控数据格式和流程
代码级区别演示
方案3典型代码(无感知):
// 和单机Session用法完全一致
HttpSession session = request.getSession();
session.setAttribute("user", user); // 自动存入Redis
方案5典型代码(全手动):
// 登录时生成并存储
String token = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(token, user.getId(), 30, TimeUnit.MINUTES);// 拦截器中验证
String token = request.getHeader("X-Token");
if(!redisTemplate.hasKey(token)) {throw new UnauthorizedException();
}
Long userId = redisTemplate.opsForValue().get(token);
如何选择?
-
选方案3如果:
- 已有传统Web应用需要改造
- 想保持原有Session API写法
- 不介意依赖Spring生态
-
选方案5如果:
- 全新设计的前后端分离系统
- 需要精细控制Session数据结构
- 追求轻量化/去框架依赖
一句话总结区别
方案3是让框架帮你搬行李的旅行社,方案5是自己打包的自助游
两者最终都到达目的地(完成分布式Session),但过程体验和自由度截然不同。
相关文章:
分布式Session
我用「餐厅点餐代码实战」帮你彻底搞懂分布式Session,看完不仅能应对面试,还能直接应用到实际开发。先记住这个核心矛盾:多服务员如何记住同一顾客的喜好? 一、从生活场景理解Session的本质 传统单机场景(小餐馆&…...
Kotlin 运算符重载
在Kotlin中,常用的运算符重载函数名如下: 1.算术操作符: 加法:plus 减法:minus 乘法:times 除法:div 取模:rem 或 mod 整数除法:floorDiv 求幂:pow 自增&…...

OpenHarmony4.1-轻量与小型系统ubuntu开发环境
因OpenHarmony官网提供包含轻量、小型与标准系统的全量代码非常宠大,解包后大概需要70G以上硬盘空间,如要编译标准系统则需要140G以上空间。 如硬盘空间有限与只使用轻量/小型OpenHarmony系统,则可以下载并直接使用本人裁剪源码过的ubuntu硬盘…...

AVR 单片机硬件供电处理
摘自AVR 单片机应用笔记:AN2519 - AVR Microcontroller Hardware Design Considerations。 2. 供电 供电设计是任何硬件设计的关键一环,直接影响到系统的性能。在设计供电时,有两个重要的方面需要考虑:ESD 防护和噪声干扰。这些内…...
LeetCode 27 移除元素
LeetCode 27 - 移除元素(Remove Element)是一个简单但经典的双指针问题,主要考察数组操作的基本功。虽然问题容易,但掌握多种解法以及衍生的变体问题对解决更复杂的操作数组问题有帮助。 题目描述 输入:整数数组 nums…...
对“预训练”的理解
预训练有什么用 传统的机器学习是偏数学的,对数据的量不做过多要求,而深度学习的项目通常是有大量的数据可供使用。 在平常的任务或者项目中,我们可能并没有大量数据,只有少量数据,在这时我们就可以通过“借用”有大…...

论文阅读:CAN GENERATIVE LARGE LANGUAGE MODELS PERFORM ASR ERROR CORRECTION?
CAN GENERATIVE LARGE LANGUAGE MODELS PERFORM ASR ERROR CORRECTION? 生成式大语言模型能否进行自动语音识别(ASR)纠错? https://arxiv.org/pdf/2307.04172 文章目录 速览常规总结通俗版 摘要(Abstract)2. 引言&a…...
Stable Diffusion(SD)系列模型及关联算法深度解析
一、基础模型架构演进 SD v1.5 核心架构:基于Latent Diffusion Model(LDM),通过VAE将图像压缩至潜空间进行扩散训练,支持512x512分辨率生成,兼容二次元与写实风格混合创作12。 训练数据&…...

FPGA开发,使用Deepseek V3还是R1(3):系统级与RTL级
以下都是Deepseek生成的答案 FPGA开发,使用Deepseek V3还是R1(1):应用场景 FPGA开发,使用Deepseek V3还是R1(2):V3和R1的区别 FPGA开发,使用Deepseek V3还是R1&#x…...
logback日志输出配置范例
logback日志输出配置范例 在wutool中,提供了logback日志输出配置范例,实现日志文件大小限制、滚动覆盖策略、定时清理等功能。 关于wutool wutool是一个java代码片段收集库,针对特定场景提供轻量解决方案,只要按需选择代码片段…...

【开源免费】基于SpringBoot+Vue.JS酒店管理系统(JAVA毕业设计)
本文项目编号 T 224 ,文末自助获取源码 \color{red}{T224,文末自助获取源码} T224,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...

Unity中动态切换光照贴图LightProbe的方法
关键代码:LightmapSettings.lightmaps lightmapDatas; LightmapData中操作三张图:lightmapColor,lightmapDir,以及一张ShadowMap 这里只操作前两张: using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI;public cl…...
linux(2)用户管理
文章目录 1. 切换用户2. 添加删除用户3.写改密码 1. 切换用户 # 切换用户名,不切换工作目录 su 用户名 # 一起切换工作目录 su - 用户名 # 退出用户 exit2. 添加删除用户 # 添加用户 sudo adduser username # 推荐sudo useradd -m -s /bin/bash 用户名-m 如果创建…...

在鸿蒙HarmonyOS手机上安装hap应用
一、下载工具 安装hap包需要用到小工具 。 二、解压到目录后,进入该文件夹,打开命令行,如下图 三、将下载好的hap包放入刚才解压的文件夹内(假设hap包文件名为app.hap) 四、连接好手机和电脑,手机需要打…...

MacBook Pro使用FFmpeg捕获摄像头与麦克风推流音视频
FFmpeg查看macos系统音视频设备列表 ffmpeg -f avfoundation -list_devices true -i "" 使用摄像头及麦克风同时推送音频及视频流: ffmpeg -f avfoundation -pixel_format yuyv422 -framerate 30 -i "0:1" -c:v libx264 -preset ultrafast -b:v 1000k -…...
工程化与框架系列(8)--持续集成实践
持续集成实践 🔄 持续集成(Continuous Integration,简称CI)是现代前端开发流程中的重要环节,它通过自动化构建、测试和部署,帮助团队更快速、更可靠地交付高质量代码。本文将详细介绍前端持续集成的实践方…...
Python核心技术,Django学习基础入门教程(附环境安装包)
文章目录 前言1. 环境准备1.1Python安装1.2选择Python开发环境1.3 创建虚拟环境1.4 安装 Django 2. 创建 Django 项目3. Django项目结构介绍4. 启动开发服务器5. 创建 Django 应用6. 应用结构介绍7. 编写视图函数8. 配置 URL 映射9. 运行项目并访问视图10. 数据库配置与模型创建…...

【Qt-信号与槽】connect函数的用法
🏠个人主页:Yui_ 🍑操作环境:Qt Creator 🚀所属专栏:Qt 文章目录 1.信号和槽的概念1.1 信号的本质1.2 槽的本质1.3 补充说明2. 信号和槽的使用2.1 connect函数介绍2.2 connect函数的简单使用2.2.1 图形化方…...

计算机毕业设计SpringBoot+Vue.js景区民宿预约系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

服务流程设计和服务或端口重定向及其websocket等应用示例
服务流程设计和服务或端口重定向及其websocket等应用示例 目录 服务或端口重定向的服务设计和websocket等应用示例 一、通用请求控制流程 1.1、入口 1.2、所有GET请求首先预检控制单元 1.3、http请求会分别自动307重定向 1.4、所有请求首先执行跨源控制单元 1.5、然后…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...

JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...