spring boot 及 redis 实现分布式session 实践笔记

(0)背景

Spring-Session实现Session共享入门教程

  1. Spring-Session使用的场景?

HttpSession是通过Servlet容器进行创建和管理的,在单机环境中。通过Http请求创建的Session信息是存储在Web服务器内存中,如Tomcat/Jetty。

假如当用户通过浏览器访问应用服务器,session信息中保存了用户的登录信息,并且session信息没有过期失,效那么用户就一直处于登录状态,可以做一些登录状态的业务操作!
单机session

但是现在很多的服务器都采用分布式集群的方式进行部署,一个Web应用,可能部署在几台不同的服务器上,通过LVS或者Nginx等进行负载均衡(一般使用Nginx+Tomcat实现负载均衡)。此时来自同一用户的Http请求将有可能被分发到不同的web站点中去(如:第一次分配到A站点,第二次可能分配到B站点)。那么问题就来了,如何保证不同的web站点能够共享同一份session数据呢?

假如用户在发起第一次请求时候访问了A站点,并在A站点的session中保存了登录信息,当用户第二次发起请求,通过负载均衡请求分配到B站点了,那么此时B站点能否获取用户保存的登录的信息呢?答案是不能的,因为上面说明,Session是存储在对应Web服务器的内存的,不能进行共享,此时Spring-session就出现了,来帮我们解决这个session共享的问题!

集群session

  1. 如何进行Session共享呢?

简单点说就是请求http请求经过Filter职责链根据配置信息过滤器将创建session的权利由tomcat交给了Spring-session中的SessionRepository,通过Spring-session创建会话,并保存到对应的地方。

session共享

实际上实现Session共享的方案很多,其中一种常用的就是使用Tomcat、Jetty等服务器提供的Session共享功能,将Session的内容统一存储在一个数据库(如MySQL)或缓存(如Redis,Mongo)中,

一句话总结——
DelegatingFilterProxy引导每个请求到名为springSessionRepositoryFilter过滤器
RedisHttpSessionConfiguration创建名为springSessionRepositoryFilter 的Spring Bean,继承自Filter
springSessionRepositoryFilter替换容器默认的HttpSession支持为Spring Session
Spring Session将Session路由至redis


(一)spring boot 实现Redis实现分布式session功能的共享


spring boot 集成 redis-session:

实践测试:
有一个业务:
步骤一:获取验证码,服务端保存验证码在session上
步骤二:点击验证,服务端拿取session上的验证码与用户输入的作比较


首先在没有redis的情况下,步骤一指向服务器1,步骤二指向服务器2,原理上应该无法验证,因为session分布在两台服务器上,
实践结果与原理相同

然后在redis情况下,步骤一指向服务器1,步骤二指向服务器2,步骤一、二仍然应该连贯顺利进行,实践结果与原理相同

再,在redis情况下,步骤一指向服务器1,步骤二指向服务器2,步骤一进行后,删掉redis清空,步骤二应该无法验证,实践结果与原理相同

@Configuration
@EnableRedisHttpSession
public class RedisSessionConfig {
 
}
 

如果需要添加失效时间可以使用以下的写法:

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 60) //1分钟失效
补充一下:
这个session的过期时间住了在注解上配置,也可以在java中设置的:
session.setMaxInactiveInterval(3 * 60);//3分钟失效




(二)9.30 出现问题: 

Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer'

根据提示,去相应的页面查看解决方案

Firing SessionDeletedEvent or SessionExpiredEvent is made available through the SessionMessageListener which listens to Redis Keyspace events. In order for this to work, Redis Keyspace events for Generic commands and Expired events needs to be enabled. For example:

redis-cli config set notify-keyspace-events Egx

If you are using @EnableRedisHttpSession the SessionMessageListener and enabling the necessary Redis Keyspace events is done automatically. However, in a secured Redis enviornment the config command is disabled. This means that Spring Session cannot configure Redis Keyspace events for you. To disable the automatic configuration add ConfigureRedisAction.NO_OP as a bean.

For example, Java Configuration can use the following:

@Bean
public static ConfigureRedisAction configureRedisAction() {
    return ConfigureRedisAction.NO_OP;
}

XML Configuration can use the following:

<util:constant
    static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
所以在redisconfig上注入bean

[java] view plain copy
  1. @Configuration  
  2. public class HttpSessionConfig {  
  3.     @Bean  
  4.     public static ConfigureRedisAction configureRedisAction() {  
  5.         return ConfigureRedisAction.NO_OP;  
  6.     }  
  7. }  

(三)原理简述

  1. 每次请求都通过NoSql数据库查询,如果存在,则获取值;反之存放值。  
  2. 我是通过redis来实现session的共享,其主要有一下两种方法:  
  3. 1、通过tomcat服务器的拓展功能实现  
  4.     这种方式比较简单,主要是通过继承session的ManagerBase类,实现重写session相关的方法,这种比较简单,  
  5.     参考源码链接(http://download.csdn.net/detail/fengshizty/9417242)。  
  6. 2、通过filter拦截request请求实现  
  7.     下面主要介绍这样实现方式:  
  8.     (1)写HttpSessionWrapper实现HttpSession接口,实现里面session相关的方法。  
  9.     (2)写HttpServletRequestWrapper继承javax.servlet.http.HttpServletRequestWrapper类,重写对于session  相关的方法。  
  10.     (3)写SessionFilter拦截配置的请求url,过去cookie中  
  11.     的sessionId,如果为空,对此次请求重写生成一个新的sessionId,在sessionId构造新的HttpServletRequestWrapper对象。  
  12.     (4)写SessionService实现session到redis的保存和过去,其key为sessionId,value为session对于的Map。  
  1.  

以上是常规实现的步骤,spring 帮我们做了

增加配置类:

[java] view plain 
  1. @EnableRedisHttpSession  
  2. public class HttpSessionConfig {  
  3.     @Bean  
  4.     public JedisConnectionFactory connectionFactory() {  
  5.             return new JedisConnectionFactory();  
  6.     }  
  7. }  
这个配置类有什么用呢?

官方文档:

The Spring configuration is responsible for creating a Servlet Filter that replaces the HttpSession implementation with an implementation backed by Spring Session. 

也就是说,这个配置类可以创建一个过滤器,这个过滤器支持Spring Session代替HttpSession发挥作用

The @EnableRedisHttpSession annotation creates a Spring Bean with the name of springSessionRepositoryFilter that implements Filter. The filter is what is in charge of replacing the HttpSession implementation to be backed by Spring Session. In this instance Spring Session is backed by Redis.

@EnableRedisHttpSession注解会创建一个springSessionRepositoryFilter的bean对象去实现这个过滤器。过滤器负责代替HttpSession。

也就是说,HttpSession不再发挥作用,而是通过过滤器使用redis直接操作Session。

(四)redis的数据

key的简单介绍说明:


# 存储 Session 数据,数据类型hash
Keyspring:session:sessions:XXXXXXX

# Redis TTL触发Session 过期。(Redis 本身功能),数据类型:String
Keyspring:session:sessions:expires:XXXXX

#执行 TTL key ,查看剩余生存时间


#定时Job程序触发Session 过期。(spring-session 功能),数据类型:Set
Keyspring:session:expirations:XXXXX

取自:

Spring-Session实现Session共享入门教程


另有一片源码分析:

Spring Session解决分布式Session问题的实现原理

https://blog.csdn.net/xlgen157387/article/details/60321984





原文地址:https://www.cnblogs.com/silyvin/p/9106765.html