session是一个非常常见的概念。session的作用是为了辅助http协议,因为http是本身是一个无状态协议。为了记录用户的状态,session机制就应运而生了。同时session也是一个非常老的概念了,使用session的方法也是多种多样。就Java来说,servlet的标准本身是包含session的,Tomcat会把session的信息存储在服务器内存里,Request提供了获取session的方法。

然而,前文所述的session机制其实是有不少缺点的。首先就是session数据没有一定的持久化机制,而且难以实现应用服务器的水平扩展。在负载均衡器 + 应用服务器集群的架构中,session共享是一个基本的要求。在Spring生态圈里,也有Spring session完成session存储和共享的功能。Spring Session支持把session信息以各种形式存储,比如数据库或者Redis。个人认为,Redis比RDBMS更加适合session数据的存储。首先,session数据都是有时效性的,Redis的数据超时机制可以很好的完成session信息的清除;此外,Redis的数据访问速度更快,对于时效性较强的session数据,会有比较好的加速效果。

由于注解的存在,在Spring项目中增加Spring Session Redis是非常的简单的:

1. 增加Spring Session以及Spring Data Redis的依赖

  1. compile 'org.springframework.session:spring-session:1.3.0.RELEASE'
  2.  
  3. compile 'org.springframework.boot:spring-boot-starter-data-redis'

2. 增加Spring Session Redis的配置

  1. @EnableRedisHttpSession
  2. public class HttpSessionConfig {
  3. @Bean
  4. public RedisConnectionFactory connectionFactory() {
  5. JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
  6. connectionFactory.setPort(6379);
  7. connectionFactory.setHostName("localhost");
  8. return connectionFactory;
  9. }
  10. }

然后就完成了,实在是太简单了。在浏览器中进行请求,然后可以发现Redis中出现如下的内容:

先不解释Redis中存储的数据的具体含义,我们先来研究一下,@EnableRedisHttpSession 的注解到底帮我们做了什么。所以,看源码走起咯。。。

  1. @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
  2. @Target({ java.lang.annotation.ElementType.TYPE })
  3. @Documented
  4. @Import(RedisHttpSessionConfiguration.class)
  5. @Configuration
  6. public @interface EnableRedisHttpSession {
  7. /* */
  8. }

这个注解包含了一个重要的@Import注解,@Import注解支持导入普通的java类,并将其声明成一个bean。所以使用了@EnableRedisSession之后,就相当于同时声明了一个RedisHttpSessionConfiguration的bean。然后看看,这个类是如何定义的:

  1. @Configuration
  2. @EnableScheduling
  3. public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
  4. implements EmbeddedValueResolverAware, ImportAware {
  5. @Bean
  6. public RedisTemplate<Object, Object> sessionRedisTemplate(
  7. RedisConnectionFactory connectionFactory) {
  8. RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
  9. template.setKeySerializer(new StringRedisSerializer());
  10. template.setHashKeySerializer(new StringRedisSerializer());
  11. if (this.defaultRedisSerializer != null) {
  12. template.setDefaultSerializer(this.defaultRedisSerializer);
  13. }
  14. template.setConnectionFactory(connectionFactory);
  15. return template;
  16. }
  17.  
  18. @Bean
  19. public RedisOperationsSessionRepository sessionRepository(
  20. @Qualifier("sessionRedisTemplate") RedisOperations<Object, Object> sessionRedisTemplate,
  21. ApplicationEventPublisher applicationEventPublisher) {
  22. RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
  23. sessionRedisTemplate);
  24. sessionRepository.setApplicationEventPublisher(applicationEventPublisher);
  25. sessionRepository
  26. .setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
  27. if (this.defaultRedisSerializer != null) {
  28. sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
  29. }
  30.  
  31. String redisNamespace = getRedisNamespace();
  32. if (StringUtils.hasText(redisNamespace)) {
  33. sessionRepository.setRedisKeyNamespace(redisNamespace);
  34. }
  35.  
  36. sessionRepository.setRedisFlushMode(this.redisFlushMode);
  37. return sessionRepository;
  38. }
  39.  
  40. }

RedisHttpSessionConfiguration中声明了几个重要的bean,上面的代码片段其实就是实现了Redis相关的配置。其中的依赖关系为RedisOperationsSessionRepository =>sessionRedisTemplate => RedisConnectionFactory。同时RedisHttpSessionConfiguration继承于SpringHttpSessionConfiguration。而SpringHttpSessionConfiguration本身会申明一个springSessionRepositoryFilter,这个filter作用于全局,session的保存和失效都是在这里完成的。对于这个filter的具体实现,这里先不作分析,因为它本身还是通过调用RedisOperationsSessionRepository来操作Redis实现session的保存的。

RedisOperationsSessionRepository实现代码其实还挺多的,但是逻辑却都不复杂,所以没有必要贴很多代码。就看看Session保存的函数吧!

  1. public void save(RedisSession session) {
  2. session.saveDelta();
  3. if (session.isNew()) {
  4. String sessionCreatedKey = getSessionCreatedChannel(session.getId());
  5. this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
  6. session.setNew(false);
  7. }
  8. }

并没有太多东西,sessionRedisOperations.convertAndSend()完成了session数据的保存。那么Redis中的session数据是如何存储的呢?对于一个session数据,会在Redis存两个key/value,其中一个存储实际的session数据,另外一个用于辅助session过期的处理。此外还有一个总体的数据,用于记录过期的session。下面是一个典型的存储形式:

  1. * HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr2:attrName someAttrValue2
  2. * EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
  3. * APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
  4. * EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
  5. * SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
  6. * EXPIRE spring:session:expirations1439245080000 2100

第一条的存储命令就是实际的session数据,这个数据在Redis是以HashSet的形式存储的,可以在里面存储各种数据。第三条存储命令就是辅助session过期的数据,它在expire时间会比实际数据久一点,用于区别一个session是超时还是不存在。第五条存储命令就是整体的session过期记录,它是一个集合。这个整体expirations集合的意义在于Redis的超时是不精确的,可能会出现一个session已经过期(辅助session数据也已经过期)了,但是它仍然存在于Redis中。这是因为Redis的数据超时清除任务是低优先级的。有了整体的session过期记录,就可以解决这种问题。当Redis真正清除数据时(会有事件通知),再把session从expirations集合中删除掉。

Spring Session加Redis(山东数漫江湖)的更多相关文章

  1. Spring Session加Redis

    session是一个非常常见的概念.session的作用是为了辅助http协议,因为http是本身是一个无状态协议.为了记录用户的状态,session机制就应运而生了.同时session也是一个非常老 ...

  2. Spring归纳小结(山东数漫江湖)

    前言 如果说有什么框架是Java程序员必然会学习.使用到的,那么Spring肯定是其中之一.本篇博客,将根据博主在日常工作中对Spring的使用做一个系统的归纳小结. Spring的一些概念和思想 S ...

  3. Spring 事务管理(山东数漫江湖)

    最新又重新学习了一遍Spring的事务,这里做点总结,不做如何一步步配置的流水账. 1. 关键类 public interface PlatformTransactionManager { Trans ...

  4. 深入分析Spring 与 Spring MVC容器(山东数漫江湖)

    1 Spring MVC WEB配置 Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext, ...

  5. Spring cookie 实战(山东数漫江湖)

    Cookie是什么 简单来说,cookie就是浏览器储存在用户电脑上的一小段文本文件.cookie 是纯文本格式,不包含任何可执行的代码.一个web页面或服务器告知浏览器按照一定规范来储存这些信息,并 ...

  6. 使用Spring Session和Redis解决分布式Session跨域共享问题

    http://blog.csdn.net/xlgen157387/article/details/57406162 使用Spring Session和Redis解决分布式Session跨域共享问题

  7. 170222、使用Spring Session和Redis解决分布式Session跨域共享问题

    使用Spring Session和Redis解决分布式Session跨域共享问题 原创 2017-02-27 徐刘根 Java后端技术 前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用 ...

  8. Spring4+SpringMVC+MyBatis整合思路(山东数漫江湖)

    1.Spring框架的搭建 这个很简单,只需要web容器中注册org.springframework.web.context.ContextLoaderListener,并指定spring加载配置文件 ...

  9. Spring Session - 使用Redis存储HttpSession例子

    目的 使用Redis存储管理HttpSession: 添加pom.xml 该工程基于Spring Boot,同时我们将使用Spring IO Platform来维护依赖版本号: 引入的依赖有sprin ...

随机推荐

  1. asp.net 后台注册(调用)JS

    1.使用Page.ClientScript.RegisterClientScriptBlock 使用 Page.ClientScript.RegisterClientScriptBlock可以防止ja ...

  2. position定位-absolute与fixed

    1. absolute 生成绝对定位元素,相对于static定位以外的第一个父元素进行定位. 2. fixed 生成绝对定位元素,相对于浏览器窗口进行定位.

  3. java 字符串—数字常用处理

    // 判断一个字符串是否都为数字 public boolean isDigit(String strNum) { return strNum.matches("[0-9]{1,}" ...

  4. perf打印调用栈的过程

    perf_prepare_sample-->perf_callchain-->get_perf_callchain 上面的调用栈会使用 perf_event_output--> 0x ...

  5. mysql表、函数等被锁住无响应的问题

    场景: 在对表或函数等进行操作的时候,如果出现无法响应的情况(排除外网的网络问题),此时极有可能被某一个线程锁定了(这是函数的情况,表的话可能是被某一个用户锁定了),锁定的原因一般都是死循环出不来,而 ...

  6. H3C SNMP配置解析

    华为交换机snmp配置 snmp-agent                                           /使能snmp服务/snmp-agent local-engineid ...

  7. VBA练习-打开文件,添加选中项,生成新表

    学习VBA,正好给财务制作一个小工具: Sub 打开人员信息表() Dim wb As Workbook, c As Integer Set wb = Workbooks.Open(, True) c ...

  8. 【bzoj1704】[Usaco2007 Mar]Face The Right Way 自动转身机 贪心

    题目描述 农夫约翰有N(1≤N≤5000)只牛站成一排,有一些很乖的牛朝前站着.但是有些不乖的牛却朝后站着.农夫约翰需要让所有的牛都朝前站着.幸运的是约翰最近买了一个自动转身机.这个神奇的机器能使K( ...

  9. Go语言【第五篇】:Go条件语句

    Go语言条件 条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为true来决定是否执行指定语句,并在条件为false的情况再执行另外的语句.下图展示了程序语言中条件语句的结构: Go语言提 ...

  10. python中元组与小括号的关系

    在学习Python 的时候.说到有两种数据类型,一种叫 列表,一种叫做元组,可以认为,元组是功能精简的列表.因为它少了列表很多功能.但是又有相识.定义他们的时候,主要是用中括号和小括号之分. 例如:定 ...