分布式系统session一致性的问题-分析-解决思路(SpringSession的整合)

如果做分布式的系统,必然会遇到session一致性的问题

  • 在传统单机web应用中,一般使用tomcat/jetty等web容器时,用户的session都是由容器管理。
    浏览器使用cookie中记录sessionid,每次发送请求的时候会带上这个sessionid,web容器根据sessionid找到当时在服务存储信息时使用的那个Map,以此判断用户是否存在会话session。
    注意:最大的问题是,session存储在web容器中,被单台服务器容器管理。
    在分布式情况下,这会导致什么?

首先回顾一下session:

  • Session是什么:
    – Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。
    – 简单来说会话,就是我给你打电话然后挂断这算一次会话;对于开发,就是从你登入这个系统到你退出这个系统也是一次会话,然后可以将从你登入这个系统到你退出这个系统时所做的操作都可以存储在session域中

回顾完,我们在分析,当我们做分布式的系统时,比如分布系统中的用户模块,用户模块也必然是集群的,假设用户模块是A模块,A1,A2是用户模块的集群。

分布式系统session一致性的问题-分析-解决思路(SpringSession的整合)_第1张图片

那么当用户请求用户模块相关服务时,会被分布式系统进行负载均衡-比如Ribbon,可能第一次请求用户模块的相关服务时请求的是A1,需要在session中存入相关的数据;在第二次请求用户服务时,可能会被负载均衡请求的时A2,那么在A2的Session并没有我们上一次请求存入的数据;这样session不一致的问题就显示出来了;

咱们分析下,解决不一致问题的思路:

1:进行session的同步复制

分布式系统session一致性的问题-分析-解决思路(SpringSession的整合)_第2张图片

  • 这一种思路确实可以,且web-server(Tomcat) 也原生是支持的,只需要修改配置文件就ok
  • 但是缺点也是很多:
  • 因为这样session同步是需要服务器之间数据传输,这样占用大量的网络带宽,同时也降低了集群所带来的业务处理能力的又是
  • 同步的时间也是一个问题,比如用户可能会在同步时间内刚好访问到正在同步的服务器,可能会拿不到数据,或者不一致,一样也是会出现数据不一致的问题
  • 集群的水平扩张收到限制 因为这样同步,任意一台web-server保存的数据都是所有web-server的session总和,受到内存限制无法水平扩展更多的web-server。
  • 总结 这种方式并不适合大型的分布式;

2:转变思路将session数据存储在客户端cookie中

分布式系统session一致性的问题-分析-解决思路(SpringSession的整合)_第3张图片

  • 这样的方式确实是可以:服务器不需存储session,用户保存自己的session信息到cookie中。节省服务端资源
  • 但是这样也同时是十分危险的:
  • 每次http请求,携带用户在cookie中的完整信息,浪费网络带宽
  • session数据放在cookie中,cookie有长度限制4K,不能保存大量信息
  • session数据放在cookie中,存在泄漏、篡改、窃取等安全隐患,且随时可能会被清理或者有些用户会关闭cookie
  • 总结:这种方式也并不适合,仅仅是思路

3:通过反向代理hash 一致性

  • 原理:利用反向代理使得同一个用户的请求落在一台固定的服务器上,不要发生服务器切换即可,服务器之前内存session中的数据还是在的;
  • 网络协议中的第四层ip代理:使用用户的ip来做hash,以保证同一个ip的请求落在同一个web-server上
    分布式系统session一致性的问题-分析-解决思路(SpringSession的整合)_第4张图片
  • 比如设定个hash函数,比如一共集群了5台webserver,通过hash将ip的尾数余%5,让他们每一次访问都是固定一台服务器
  • 不仅可以在第四层,也可以七层七层代理根据http协议任意业务字段自定义hash,比如sid,city_id,user_id等,能够更加灵活的实施hash策略,以保证同一个浏览器用户的请求落在同一个web-server上
    分布式系统session一致性的问题-分析-解决思路(SpringSession的整合)_第5张图片
  • 通过反向代理的优点:只需要改nginx配置,不需要修改应用代码
    负载均衡,只要hash属性的值分布是均匀的,多台web-server的负载是均衡的
    可以支持web-server水平扩展(session同步法是不行的,受内存限制)
  • 缺点也有:
  • 因为每一台session都是在自己的服务器上,并没有进行同步,当web-server重启可能导致部分session丢失,影响业务,如部分用户需要重新登入
  • 如果需要进行水平扩张,所以hash函数也需要重新设置:session也需要重新分布,也会有一部分用户路由不到正确的session
    – 不过以上缺点问题也不是很大,因为session本来都是有有效期的。所以这两种反向代理的方式可以使用

4:由后端统一存储session数据

  • 这时我们就想到了缓存数据库Redis;
  • 分布式系统session一致性的问题-分析-解决思路(SpringSession的整合)_第6张图片
  • 利用Redis存储层,将session存储在web-server后端的存储层,数据库或者缓存;session是频繁读取的数据而且有过期时间,也十分适合存在Redis缓存中,十分符合Redis的特性
  • Redis的特性:
    性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
    – 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
    原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
    丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
  • 所以这种方式也是十分适合分布式系统的开发,集群水平扩张和Redis的水平扩张都不会收到影响

在Spring的架构中:SpringSession主要解决的就是session一致性的问题;

  • 使用SpringSession,所有的session由SpringSession创建维护,无需我们修改任何代码,就能在集群环境下使用原生的session方式编程,无侵入、简单配置和Spring应用无缝整合、对接各种session存储方案;
  • 简单来说用法都是一样的;但是通过简单的配置,存储的数据是在你配置的Redis缓存中

好啦,同时SpringSession的配置也是很简单

  • 引入依赖
  • 因为是在redis中存储session,所以redis也是得引入,然后引入springboot和springboot的整合
<!-- 引入springboot&redis整合场景 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 引入springboot&springsession整合场景 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
  • 在配置文件中-application.properties配置相关的配置
# redis配置
spring.redis.host=xx.xxx.xxx
#spring.redis.pool.max-active=100 
spring.redis.jedis.pool.max-idle=100
# springsession配置
spring.session.store-type=redis
 
# session过期时间
spring.session.timeout=1800

然后创建一个TestController 测试下就好啦

@GetMapping("/set")
public String setSession(HttpSession session){
session.setAttribute("msg", "Hello");
return "ok";
}
 
@GetMapping("/get")
public String getSession(HttpSession session){
return (String) session.getAttribute("msg");
}
  • 如果配置成功了的话是:看到redis中保存了数据,而且如果我们设置多台服务器,访问get获取到的都是一样;不成功得检查下配置文件哦

okok!如果有帮助到大家给个赞呗!

你可能感兴趣的:(springboot,springcloud,java)