分布式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、然后…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
