分布式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、然后…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...