当前位置:网站首页>面试难题:分布式 Session 实现难点,这篇就够!
面试难题:分布式 Session 实现难点,这篇就够!
2022-07-30 21:12:00 【androidstarjack】
点击上方关注 “终端研发部”
设为“星标”,和你一起掌握更多数据库知识
来源:blog.csdn.net/Gaowumao/article/details/124309548
那如何来解决分布式Session问题呢?
一、解决方案列举
二、Java代码实现解决分布式Session
平常做的项目都是在一台应用系统,并且所有的操作都在一台
Tomcat服务器上,并不会引发Session共享的问题,所以并不会对我们的系统产生影响,但是当我们部署多个微服务的时候,再搭配Nginx进行负载均衡时,如果不处理分布式Session问题,我们在系统中访问不同功能时就会频繁出现用户登录的操作
图解分析原因:

前提:用户登录功能和图中的商品订单模块、秒杀抢购模块属于单独的微服务模块用户登录成功后想要访问图中其他两个模块的功能时,由于Nginx使用默认负载均衡策略(轮询 ),这时请求会按照时间顺序逐一分发到后端应用上,也就是说用户在Tomcat1上登录成功之后,用户的信息放在Tomcat1的Session里,过了一会,用户想要进行秒杀活动的功能操作,请求又被Nginx分发到了Tomcat2,而这时的Tomcat2上的Session里还没有用户信息,于是就是出现让用户重新登录的情况,在微服务分布式项目中,不同的功能模块必然会被分列成各自的微服务,假设访问一个功能都需要重新登录一次,用户的体验必然会大幅度下降!
那如何来解决分布式Session问题呢?
一、解决方案列举
1. Session复制
优点:
无需修改代码,只需要修改Tomcat配置
缺点:
Session同步传输占用内网宽带
多台Tomcat同步性能指数级下降
Session占用内存,无法有效水平扩展
2. 前端存储
优点:
不占用服务器内存
缺点:
存在安全风险
数据大小受cookie限制
占用外网宽带
3. Session粘滞
优点:
无需修改代码
服务端可以水平扩展
缺点:
增加新机器,会重新Hash,导致重新登录
应用重新启动后,需要重新登录
4. 后端集中存储
优点:
安全
容易水平扩展
优点:
增加复杂度
需要修改代码
二、Java代码实现解决分布式Session
1. SpringSession - Redis解决分布式Session
添加依赖
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--commons-pools2 对象池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--spring session 依赖-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>添加Redis配置
## Redis配置
spring:
redis:
服务器地址
host: localhost
端口
port: 6379
数据库
database: 0
超时时间
connect-timeout: 10000ms
lettuce:
pool:
最大连接数
max-active: 8
最大连接阻塞等待时间 默认 -1
max-wait: 10000ms
最大空闲时间 默认8
max-idle: 200
最小空闲连接 默认8
min-idle: 5业务逻辑实现
/**
* 登录功能
* @param loginVo
* @return
*/
@Override
public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
String username = loginVo.getUserName();
String password = loginVo.getPassword();
User user = userMapper.selectByUserName(username);
if (user == null){
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
//判断密码是否正确
if (!MDUtils.formPassToDBPass(password,user.getSalt()).equals(user.getPassword())){
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
//使用UUID生成字符串代替Cookie
String ticket = UUIDUtil.uuid();
request.getSession().setAttribute(ticket,user);
CookieUtil.setCookie(request,response,"userTicket",ticket);
return RespBean.success();
}视图控制层
/**
* 跳转商品列表
* @param session
* @param model
* @return
*/
@RequestMapping("/toList")
public String toList(HttpSession session, Model model,@CookieValue("userTicket")String ticket){
if (StringUtils.isEmpty(ticket)){
return "login";
}
User user = (User) session.getAttribute(ticket);
if (user == null){
return "login";
}
model.addAttribute("user",user);
return "goodsList";
}登录测试

打开Redis管理软件发现Session信息已经添加到Redis中了
2. Redis解决分布式Session
导入依赖
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--commons-pools2 对象池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>业务逻辑层
@Override
public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
String username = loginVo.getUserName();
String password = loginVo.getPassword();
User user = userMapper.selectByUserName(username);
if (user == null){
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
//判断密码是否正确
if (!MDUtils.formPassToDBPass(password,user.getSalt()).equals(user.getPassword())){
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
//成功Cookie
String ticket = UUIDUtil.uuid();
//将用户信息存入到redis中
redisTemplate.opsForValue().set("userTicket",ticket);
redisTemplate.opsForValue().set("user:"+ticket,user);
//request.getSession().setAttribute(ticket,user);
CookieUtil.setCookie(request,response,"userTicket",ticket);
return RespBean.success();
}
/**
* 根据cookie获取cookie
* @param ticket
* @return
*/
@Override
public User getUserByByCookie(String ticket,HttpServletRequest request,HttpServletResponse response) {
if (StringUtils.isEmpty(ticket)){
return null;
}
User user = (User) redisTemplate.opsForValue().get("user:" + ticket);
if (user == null){
CookieUtil.setCookie(request,response,"userTicket",ticket);
}
return user;
}视图控制层
/**
* 跳转商品列表
* @param session
* @param model
* @return
*/
@RequestMapping("/toList")
public String toList(HttpSession session, Model model,HttpServletRequest request,HttpServletResponse response){
String ticket = (String) redisTemplate.opsForValue().get("userTicket");
if (StringUtils.isEmpty(ticket)){
return "login";
}
//User user = (User) session.getAttribute(ticket);
User user = userService.getUserByByCookie(ticket, request, response);
if (user == null){
return "login";
}
model.addAttribute("user",user);
return "goodsList";
}测试成功

查看Redis管理工具


写在最后的话
大家看完有什么不懂的可以在下方留言讨论,也可以私信问我一般看到后我都会回复的。最后觉得文章对你有帮助的话记得点个赞哦,点点关注不迷路
@终端研发部
每天都有新鲜的干货分享!
回复 【idea激活】即可获得idea的激活方式
回复 【Java】获取java相关的视频教程和资料
回复 【SpringCloud】获取SpringCloud相关多的学习资料
回复 【python】获取全套0基础Python知识手册
回复 【2020】获取2020java相关面试题教程
回复 【加群】即可加入终端研发部相关的技术交流群
阅读更多
用 Spring 的 BeanUtils 前,建议你先了解这几个坑!
lazy-mock ,一个生成后端模拟数据的懒人工具
在华为鸿蒙 OS 上尝鲜,我的第一个“hello world”,起飞!
字节跳动一面:i++ 是线程安全的吗?
一条 SQL 引发的事故,同事直接被开除!!
太扎心!排查阿里云 ECS 的 CPU 居然达100%
一款vue编写的功能强大的swagger-ui,有点秀(附开源地址)
相信自己,没有做不到的,只有想不到的在这里获得的不仅仅是技术!
点“在看”支持小于哥呀,谢谢啦边栏推荐
猜你喜欢

数据指标口径不统一、重复开发?亿信ABI指标管理平台帮你解决

7、MySQL Workbench 导出导入数据库

外包干了三年,废了...

使用map函数,对list中的每个元素进行操作 好像不用map

ENS emoji domain name is on fire!Hype or opportunity?
![[Limited Time Bonus] 21-Day Learning Challenge - MySQL from entry to mastery](/img/12/f9fe60c7fc3d376aa95a4756541b61.png)
[Limited Time Bonus] 21-Day Learning Challenge - MySQL from entry to mastery

LeetCode·每日一题·952.按公因数计算最大组件大小·并查集

MySQL 多表关联一对多查询实现取最新一条数据

GPGGA NTRIP RTCM 笔记

canvas基础讲解加示例
随机推荐
数据指标口径不统一、重复开发?亿信ABI指标管理平台帮你解决
C语言犄角旮旯的知识之结构体
[Deep Learning] Understanding of Domain Adaptation in Transfer Learning and Introduction of 3 Techniques
【信息安全技术】RSA算法的研究及不同优化策略的比较
MySQL 视图(详解)
Automatically generate test modules using JUnit4 and JUnitGenerator V2.0 in IDEA
HJ85 longest palindrome substring
MVC模式和三层架构
Oblique document scanning and character recognition (opencv, coordinate transformation analysis)
对List集合中每个对象元素按时间顺序排序
mysql deadlock
MySQL----多表查询
Cookie中的JSESSIONID说明
MySQL笔记1(数据库的好处,数据库的概念,数据库的特点,MySQL的启动,数据模型,SQL)
socket: Kernel initialization and detailed process of creating streams (files)
[Typora] This beta version of Typora is expired, please download and install a newer version.
IDEA2018.3.5取消双击Shift快捷键
mysql死锁
tcp协议传输中的粘包问题
使用map函数,对list中的每个元素进行操作 好像不用map