• spring boot 分布式session实现


    spring boot 分布式session实现

    主要是通过包装 HttpServletRequest 将 session 相关的方法进行代理。

    具体是的实现就是通过 SessionRepositoryFilter 过滤器将 HttpServletRequest 对象进行包装,当调用 session 相关的方法时,代理到 SessionRepository 的实现类。

    我们先看看 SessionRepository 。

    public interface SessionRepository {
    	//创建session
    	S createSession();
    	//保存session
    	void save(S session);
    	//通过session Id查找session
    	S findById(String id);
    	//通过session Id删除session
    	void deleteById(String id);
    
    }

    SessionRepository 是一个接口,主要用来管理 session 。各种分布式 session 处理方案都需要实现这个接口来实现具体的处理。

    SessionRepositoryFilter 是一个过滤器,它的构造方法会接收一个 SessionRepository 的实现类,并且在它的filter方法中会对 HttpServletRequest 、 HttpServletResponse 进行包装,当后续调用到 session 相关的方法时,最终都会调用到 SessionRepository 方法。

    SessionRepositoryFilter 继承了 OncePerRequestFilter

    OncePerRequestFilter 是一个抽象类,需要子类来实现 doFilterInternal 方法来实现。这个抽象类主要用来控制每个 filter 只执行一次。在它的 doFilter 方法中,又会调用到 doFilterInternal 这个抽象方法

    这个是 SessionRepositoryFilter 的构造方法

    public SessionRepositoryFilter(SessionRepository sessionRepository) {
    		if (sessionRepository == null) {
    			throw new IllegalArgumentException("sessionRepository cannot be null");
    		}
    		this.sessionRepository = sessionRepository;
    	}

    这个是 SessionRepositoryFilter 的 doFilterInternal 方法,在这个方法中可以看到分别将 request , response 进行了包装,在这之后获取的 request , response 实际上是 SessionRepositoryRequestWrapper 、 SessionRepositoryResponseWrapper 类型。

    @Override
    	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    			throws ServletException, IOException {
    		request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
    
    		SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
    		SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
    				response);
    
    		try {
    			filterChain.doFilter(wrappedRequest, wrappedResponse);
    		}
    		finally {
    			wrappedRequest.commitSession();
    		}
    	}

    之后调用 request.getSesion() 之类 session 相关的方法实际都会调用到 SessionRepositoryRequestWrapper 的方法。

    SessionRepositoryRequestWrapper 是 SessionRepositoryFilter 的内部类,所以虽然在 doFilterInternal 方法中创建 SessionRepositoryRequestWrapper 对象时,没有传递 SessionRepository ,但它依旧是可以使用的。

    下面简单看下 SessionRepositoryRequestWrapper 的 getSession 方法

    @Override
    		public HttpSessionWrapper getSession() {
    			return getSession(true);
    		}
    
    		@Override
    		public HttpSessionWrapper getSession(boolean create) {
    		  ......
          //不相关的代码已经省略,如果对应的session已经存在,就会从上面省略的地方返回对应的session。
          //如果session不存在,就会在下面去创建session。
          //可以看到这里是通过SessionRepositoryFilter.this.sessionRepository来创建的
    			S session = SessionRepositoryFilter.this.sessionRepository.createSession();
    			session.setLastAccessedTime(Instant.now());
    			currentSession = new HttpSessionWrapper(session, getServletContext());
    			setCurrentSession(currentSession);
    			return currentSession;
    		}

    spring的文档也写了如何使用redis和数据库来实现分布式session。当然spring也已经实现了redis和数据库的具体实现。我们仅仅使用配置就可以来使用。

    具体的文档可以查看这里https://docs.spring.io/spring-session/docs/2.2.x/reference/html/httpsession.html#httpsession-redis-jc

    比如使用redis来做分布式session

    我们只需要进行下面几步

    1、配置redis连接的相关信息

    2、通过配置启动redis session相关

    上面我分别标注了1、2、3。

    我们分别来看看。

    • 标注1:先看下 EnableRedisHttpSession 注解

    这个类会通过 Import 注解导入 RedisHttpSessionConfiguration 类。而 RedisHttpSessionConfiguration 类又是继承了 SpringHttpSessionConfiguration 。

    在 RedisHttpSessionConfiguration 中会实现具体的 session 管理的相关工作。它会创建一个类型为 RedisIndexedSessionRepository 的bean。这个bean就实现了我们开头提到的 SessionRepository 接口,用来执行具体的 session 管理的相关工作。比如将 session 保存到 redis ,从 redis 查找、删除对应 session 等等具体的工作。

    在 SpringHttpSessionConfiguration 中会通过注入上面创建的 RedisIndexedSessionRepository 的 bean ,创建 SessionRepositoryFilter 过滤器。

    各种分布式实现方案一般都是通过这种方式来实现的。实现具体的 session 管理工作。通过 SpringHttpSessionConfiguration 来完成其他工作。

    使用数据库做分布式session的时候也是继承 SpringHttpSessionConfiguration 。

    • 标注2:这个是通过我们在yml中的配置来得到redis的连接工厂

    • 标注3:这个主要是用来指定redis序列化的实现。

    上面的是 RedisHttpSessionConfiguration 的方法,在创建 RedisTemplate 、 RedisIndexedSessionRepository 时,都会判断 defaultRedisSerializer 是否为null,不是null的情况下,会设置到 RedisTemplate 、 RedisIndexedSessionRepository 上去。默认的序列化实现,在我们在redis直接查看的时候,就会显示乱码。如下图:

    注意:这里我们可以看到 RedisTemplate 并不是通过注入的方式来实现的。所以我们在外面创建 RedisTemplate 的bean对象,在这里时用不到的。

    所以就需要我们通过指定序列化实现,注入到 defaultRedisSerializer 属性上。在 RedisHttpSessionConfiguration 这个类中正好有注入的方法:

    所以我们就可以在我们的代码中生成 RedisSerializer 类型的 bean ,同时指定 bean 的名字为 springSessionDefaultRedisSerializer ,就可以注入上去。

    现在我们在redis查看session时,就不是乱码了。

    其他如使用数据库或其他方案来实现分布式session,基本都和redis是类似的。

    不过由于各种数据库的语法、等等各方面会稍有差异,所以每个数据库的session的建表语句都是不同的。如文档上所说,需要指定数据库类型和建表脚本。

  • 相关阅读:
    HTML+CSS个人静态网页设计
    Vue框架的学习(Vue操作指令学习三 V-bind )第三课
    Android studio 断点调试、日志断点
    14 - 多线程之锁优化(下):使用乐观锁优化并行操作
    架构师的 36 项修炼1 开篇词:7分钟Get技术人进阶技巧
    3.NVIDIA Deepstream开发指南中文版--Deepstream 环境配置
    创意电子学-小知识:晶体管
    基于北方苍鹰优化的BP神经网络(分类应用) - 附代码
    【python】Numpy统计函数总结
    进程同步与互斥
  • 原文地址:https://blog.csdn.net/m0_71777195/article/details/126567330