分布式session-SpringSession的应用

宅哥聊构架 后端 2024-09-26

分布式session-SpringSession的应用

springsession的特性

Spring Session提供了一套创建和管理Servlet HttpSession的方案,默认采用外置的Redis来存储Session数据,以此来解决Session共享的问题。

Spring Session提供以下特性:

  • API和用于管理用户会话的实现;
  • 允许以应用程序容器(即Tomcat)中性的方式替换HttpSession;
  • Spring Session 让支持集群会话变得不那么繁琐
  • Spring session支持在单个浏览器实例中管理多个用户的会话。
  • Spring Session 允许在headers 中提供会话ID以使用RESTful API。

实现原理

Spring-Session的实现就是设计一个过滤器SessionRepositoryFilter ,SessionRepositoryFilter 会先拦截到请求,将 request 和 response 对象转换成 SessionRepositoryRequestWrapper 和SessionRepositoryResponseWrapper 。后续当第一次调用 request 的getSession方法时,会调用到 SessionRepositoryRequestWrapper 的getSession方法。

如果从request中的属性中查找不到session,再通过cookie拿到sessionid去redis中查找,如果差查不到,就直接创建一个redissession对象,并同步到redis中。将创建销毁session的过程从服务器转移到redis中去。

部分源码java

代码解读
复制代码
/** * HttpServletRequest getSession()实现 */ @Override public HttpSessionWrapper getSession() { return getSession(true); } @Override public HttpSessionWrapper getSession(boolean create) { HttpSessionWrapper currentSession = getCurrentSession(); if (currentSession != null) { return currentSession; } //从当前请求获取sessionId String requestedSessionId = getRequestedSessionId(); if (requestedSessionId != null && getAttribute(INVALID_SESSION_ID_ATTR) == null) { S session = getSession(requestedSessionId); if (session != null) { this.requestedSessionIdValid = true; currentSession = new HttpSessionWrapper(session, getServletContext()); currentSession.setNew(false); setCurrentSession(currentSession); return currentSession; } else { // This is an invalid session id. No need to ask again if // request.getSession is invoked for the duration of this request if (SESSION_LOGGER.isDebugEnabled()) { SESSION_LOGGER.debug( "No session found by id: Caching result for getSession(false) for this HttpServletRequest."); } setAttribute(INVALID_SESSION_ID_ATTR, "true"); } } if (!create) { return null; } if (SESSION_LOGGER.isDebugEnabled()) { SESSION_LOGGER.debug( "A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for " + SESSION_LOGGER_NAME, new RuntimeException( "For debugging purposes only (not an error)")); } //为当前请求创建session S session = SessionRepositoryFilter.this.sessionRepository.createSession(); //更新时间 session.setLastAccessedTime(System.currentTimeMillis()); //对Spring session 进行包装(包装成HttpSession) currentSession = new HttpSessionWrapper(session, getServletContext()); setCurrentSession(currentSession); return currentSession; } /** * 根据sessionId获取session */ private S getSession(String sessionId) { S session = SessionRepositoryFilter.this.sessionRepository .getSession(sessionId); if (session == null) { return null; } session.setLastAccessedTime(System.currentTimeMillis()); return session; } /** * 从当前请求获取sessionId */ @Override public String getRequestedSessionId() { return SessionRepositoryFilter.this.httpSessionStrategy .getRequestedSessionId(this); } private void setCurrentSession(HttpSessionWrapper currentSession) { if (currentSession == null) { removeAttribute(CURRENT_SESSION_ATTR); } else { setAttribute(CURRENT_SESSION_ATTR, currentSession); } } /** * 获取当前请求session */ @SuppressWarnings("unchecked") private HttpSessionWrapper getCurrentSession() { return (HttpSessionWrapper) getAttribute(CURRENT_SESSION_ATTR); }

结合SpringBoot的使用

1. 导入maven依赖java

代码解读
复制代码
<!--依赖 data-redis的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--不能忘记这个依赖--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!--添加cache的依赖信息--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!--添加 session的依赖--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <!--解决spring-session处理缓存时乱码的问题--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.69</version> </dependency>

2. 编写配置类

我们这里采用使用redis存储session数据实现session共享properties

代码解读
复制代码
spring: # 配置Redis的使用 redis: database: 1 # 所使用的数据库 默认是0 host: localhost #所使用的redis的主机地址 port: 7000 # 端口号 默认是 6379 password: # 密码 timeout: 5000 # 超时时间 5000毫秒 # 连接池 lettuce 的配置 lettuce: pool: max-active: 100 min-idle: 10 max-wait: 100000 # 配置session的相关信息 session: store-type: redis # 配置存储的类型 timeout: 3600 # 配置过期时间 redis: flush-mode: on_save # 保存时刷新 namespace: springSession # 命令空间 server: port: 8081 servlet: context-path: /session

3.编写controller进行调用java

代码解读
复制代码
@RestController public class SessionController { @Value("${server.port}") private String port; @RequestMapping("/createSession") public String createSession(HttpSession httpSession){ String sessionId=httpSession.getId(); httpSession.setAttribute("name",port+sessionId); httpSession.setAttribute("sname",port+":abc"); return sessionId+"创建端口号是:"+port+"的应用创建Session,属性是:"+httpSession.getAttribute("name").toString(); } @RequestMapping("/getSession") public String getSession(HttpSession httpSession){ return "访问端口号是:"+port+",获取Session属性是:"+httpSession.getAttribute("name").toString(); } }

4. 解决session在redis中存储乱码

前面已经导入fastjson,再加上这个配置类java

代码解读
复制代码
@Configuration @EnableRedisHttpSession public class RedisSessionConfig { @Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer() { // 使用 FastJsonRedisSerializer 来序列化和反序列化redis 的 value的值 FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<>(Object.class); ParserConfig.getGlobalInstance().addAccept("com.muzz"); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setCharset(StandardCharsets.UTF_8); serializer.setFastJsonConfig(fastJsonConfig); return serializer; } }

5.打包查看效果

使用maven工具中的package需要启动两个端口来模拟效果,首先启动一个创建session分布式session-SpringSession的应用进入到jar包打包的位置 ,进入cmd命令行java

代码解读
复制代码
java -jar xxx.jar --server.port=8081

启动另一个端口访问即可以下是redis存储的session数据分布式session-SpringSession的应用

小结

本篇主要介绍SpringSession的特性,结合springboot中的应用,以及多端口实现session共享的访问,下篇我们探究一下SpringSession的设计结构之妙。

转载来源:https://juejin.cn/post/7233682328840306725

Apipost 私有化火热进行中

评论