likes
comments
collection
share

重新认识分布式架构下的session,spring实现方案的详解

作者站长头像
站长
· 阅读数 31

背景

近期我们的一个产品在客户环境做部署实施工作时,客户方要求接入他们的SSO。集成过程中,他们给了一个集成的SDK,其中的登录状态认证是基于session的。而我们产品是基于springcloud框架,前后端分离的。

借此机会我们重新再系统的熟悉下session,以及在分布式架构下的session处理策略。

重新认识分布式架构下的session,spring实现方案的详解

session正在被淘汰吗?

当我们搜索“分布式部署的session方案”时,你可能会先看到类似:

“session正在被淘汰吗”、“分布式部署下,Session使用变少的原因”。

我们是不是对session的理解还停留在大学课堂

我们以前所认识的,是狭义上的session概念,一般指的是网站设计,大概率是指的HTTP。

Http是无状态的,为了在Http协议中,保持会话状态,比如用户的登录状态、购物车等,需要有一种方案,能够把用户这一次次的请求(无状态的Http请求)关联起来。

这种技术叫session。

理论上,只要我们能够用一种方案,实现把客户这一次次的无状态Http请求关联起来,都应该算作session的一种实现。

集群/分布式环境下session处理策略

  • 在Cookie里放个JESSIONID,在服务器中存上状态,用户请求来了,根据JESSIONID去服务器里查状态,这是Tomacat的实现方法
  • 把所有状态都存在Cookie里,服务器给个签名防止伪造,每次请求来了,直接从Cookie里提取状态,这是JWT的实现方法
  • 在Cookie里放个token,状态存储基于Redis来实现
  • 根据sessionId将session信息存储到数据库
  • Spring sesssion的集成方案,spring session本身提供了一种透明的方式来进行session数据的管理,支持多种存储方式,也包括redis。

分布式下不适用或者被淘汰的是基于tomcat等web中间件的方案

在负载均衡的集群、分布式部署下:

1.浏览器发出请求,服务器访问nginx时,他会做负载均衡,选择哪个服务器主要看策略。一旦策略路由到了新的服务器,就会导致session失效。

粘性session

一开始有采用这种方案的,即标记浏览器第一次发起请求的路径,保证下一次请求,分配到相同的服务器,但是这种方案,很难保证服务器是负载均衡的

同步session

当一个请求到服务器后,会把session同步到其他的服务器,来保证所有服务器的session是统一的,但是在合众对服务器的性能会产生影响,且服务器器之间会增加耦合度

session共享

单独配备一台服务器,专门创建和获取session,所有服务器都通过这台服务器处理session,但是这样就增加了故障点。

所以我们一般主流上都会采用redis集中管理session,其读写效率高,且可以做redis的集群高可用。

Spring Boot/Cloud实现多服务session共享详解

基于以上我们的讨论,我们再来重点讨论下这个方案。实际应用中,结合我们产品的技术实现方案和客户给的SDK包,我们不想重写客户的sdk实现方法,基于spring session来实现透明方式的session管理是最好的实践路径。

实现方法详解

  1. 导入依赖
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
  1. Redis存储类型配置
spring:
  redis:
    host: 127.0.0.1
    port: 6379
  
  session: 
    store-type: redis  # Session存储类型为REDIS

这里store-type提供多种Session Store实现:

- RedisStore:将Session存储在Redis中,多台服务器共用一个Redis实现Session共享。这是默认推荐的方案。

- HazelcastStore:将Session存储在Hazelcast缓存中,多台服务器使用同一个Hazelcast集群实现Session共享。

- MongoDBStore:将Session存储在MongoDB文档数据库中,多台服务器使用同一个MongoDB实现Session共享。

- JDBCStore:将Session存储在关系型数据库中,多台服务器使用同一个数据库实现Session共享。
  1. 创建config类,实现spring session的支持
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class SessionConfig {
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new GenericFastJsonRedisSerializer();
    }
}

还可以把该注解@@EnableRedisHttpSession注解加在启动类的上面。

spring session的生命周期

这跟我们之前理解的狭义的session生命周期是有区别的:

1、创建Session:当用户第一次访问网站时,一个全新的Session会被创建,同时也会在配置的后端存储(比如Redis)中创建一个key,value就是新创建的Session对象。

2、会话活动:每当用户发送一个请求,如果还在会话超时时间内,会话就会变为活动状态。这将更新在Redis中的键的过期时间戳,确保它不会过期。

3、会话非活动:如果用户在一定时间内没有发送任何请求,会话变为非活动状态。这不会更新Redis key的过期时间戳。如果key在过期时间内没有再次变为活动状态,它最终会过期,session会被销毁。

4、显式销毁:当调用HttpServletRequest#logout() 或 HttpSession#invalidate()时,Session会被销毁。这也会导致Redis中的key被删除。

5、key过期:如果在会话超时时间内Session没有变为活动状态,Redis key会过期并被删除。这也会销毁与之对应的Session。

Spring Session引入了“非活动”状态,并且真正的Session销毁时间也推迟到了Redis key的过期时间。这带来的好处是可以配置一个较长的全局session过期时间,而通过续期使活动Session保持有效。非活动的Session会在较短的时间内过期,这有利于节省服务器资源。

另外,使用Spring Session还需要注意的地方是:必须配置Session超时时间与Redis key过期时间保持一致,否则会出现Session被错误销毁或未及时销毁的问题。

在application.properties中配置方法:

spring.session.timeout=30m 
# 设置redis的过期时间为30分钟 
spring.session.redis.timeout=1800

原理详解

请求链路

重新认识分布式架构下的session,spring实现方案的详解 tips:引用自:www.cnblogs.com/crazymakerc…

原理图

重新认识分布式架构下的session,spring实现方案的详解 tips:引用自;www.cnblogs.com/h--d/p/1485…

spring session是如何工作的

以redis作为会话存储为例,我们进行代码的跟踪,都做了哪些事情:

从注解@EnableRedisHttpSession开始,我们看到注解import了RedisHttpSessionConfiguration类,向Spring容器提供了RedisOperationsSessionRepository,其继承了SpringHttpSessionConfiguration类,在SpringHttpSessionConfiguration中,该配置类通过@Bean注解,向Spring容器中注册了一个SessionRepositoryFilter

看到filter,我们凭借程序员的直觉应该就是跟踪到核心了,我们看到过滤器主要作用是拦所有的请求,接管创建和管理Session数据。

重新认识分布式架构下的session,spring实现方案的详解 重新认识分布式架构下的session,spring实现方案的详解

重新认识分布式架构下的session,spring实现方案的详解

重新认识分布式架构下的session,spring实现方案的详解

SessionRepository被注入

我们看到注册这个filter时需要一个SessionRepository参数,在SpringHttpSessionConfiguration的继承类RedisHttpSessionConfiguration中,SessionRepository被注入。

重新认识分布式架构下的session,spring实现方案的详解

这里,RedisTemplate依赖一个RedisConnectionFactory是需要我们进行配置的。以springboot为例,只需要指定application.properties的spring.redis.cluster.nodes即可配置一个redis集群JedisConnectionFactory。

SessionRepositoryFilter如何接管创建和管理Session数据

它使用了一个SessionRepositoryRequestWrapper类接管了Http Session的创建和管理工作。

每当有请求进入时,过滤器会首先将ServletRequest 和ServletResponse 这两个对象转换成Spring内部的包装类SessionRepositoryRequestWrapper和SessionRepositoryResponseWrapper对象。

 public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
         
         request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
          //使用HttpServletRequest 、HttpServletResponse和servletContext创建一个SessionRepositoryRequestWrapper
         SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
                 request, response, this.servletContext);
         SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
                 wrappedRequest, response);
         try {
             filterChain.doFilter(wrappedRequest, wrappedResponse);
         }
         finally {
             //保存session信息
             wrappedRequest.commitSession();
         }
     }
 }

SessionRepositoryRequestWrapper类

将Sesison对象包装成了HttpSessionWrapper,目的是当Session失效时可以从sessionRepository删除。

这里重写了getSession方法,也就是为什么每当执行HttpServletRequest执行.getSession()方法后就会刷新session的过期时间。

重新认识分布式架构下的session,spring实现方案的详解

重新认识分布式架构下的session,spring实现方案的详解

redis如何保存session数据

接下来我们再来看另一个关键的节点,session数据又是怎么存放到redis中的呢:

重新认识分布式架构下的session,spring实现方案的详解

从上述代码中看到,在SessionRepositoryFilter的doFilterInternal方法最后有一个finally中执行了:

wrappedRequest.commitSession();

这里就是保存session数据到redis。commitSession()方法还会在过滤器结束后调用,用来更新Session。

至此,我们就了解到了从注解@EnableRedisHttpSession开始的各依赖、创建和管理Session数据,以及session数据是如何存储到redis中的。

总结

本文我们在分布式、集群架构发展的场景下,重新探讨了对于session的定义和理解;在分布式、集群架构下,session的实现方案;spring session的使用方法和原理详解。

转载自:https://juejin.cn/post/7363193808521740327
评论
请登录