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的依赖

    compile 'org.springframework.session:spring-session:1.3.0.RELEASE'

    compile 'org.springframework.boot:spring-boot-starter-data-redis'

2. 增加Spring Session Redis的配置

@EnableRedisHttpSession
public class HttpSessionConfig {
@Bean
public RedisConnectionFactory connectionFactory() {
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
connectionFactory.setPort(6379);
connectionFactory.setHostName("localhost");
return connectionFactory;
}
}

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

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

@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target({ java.lang.annotation.ElementType.TYPE })
@Documented
@Import(RedisHttpSessionConfiguration.class)
@Configuration
public @interface EnableRedisHttpSession {
/* */
}

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

@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements EmbeddedValueResolverAware, ImportAware {
@Bean
public RedisTemplate<Object, Object> sessionRedisTemplate(
RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
if (this.defaultRedisSerializer != null) {
template.setDefaultSerializer(this.defaultRedisSerializer);
}
template.setConnectionFactory(connectionFactory);
return template;
} @Bean
public RedisOperationsSessionRepository sessionRepository(
@Qualifier("sessionRedisTemplate") RedisOperations<Object, Object> sessionRedisTemplate,
ApplicationEventPublisher applicationEventPublisher) {
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
sessionRedisTemplate);
sessionRepository.setApplicationEventPublisher(applicationEventPublisher);
sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
if (this.defaultRedisSerializer != null) {
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
} String redisNamespace = getRedisNamespace();
if (StringUtils.hasText(redisNamespace)) {
sessionRepository.setRedisKeyNamespace(redisNamespace);
} sessionRepository.setRedisFlushMode(this.redisFlushMode);
return sessionRepository;
} }

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

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

    public void save(RedisSession session) {
session.saveDelta();
if (session.isNew()) {
String sessionCreatedKey = getSessionCreatedChannel(session.getId());
this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
session.setNew(false);
}
}

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

 * HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr2:attrName someAttrValue2
* EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
* APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
* EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
* SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
* 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 Session和Redis解决分布式Session跨域共享问题

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

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

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

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

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

  5. spring session 加载的时候一些配置问题

    启动springboot时候的错误信息: An attempt was made to call the method org.springframework.boot.autoconfigure.s ...

  6. Nginx+Tomcat搭建集群,Spring Session+Redis实现Session共享

    小伙伴们好久不见!最近略忙,博客写的有点少,嗯,要加把劲.OK,今天给大家带来一个JavaWeb中常用的架构搭建,即Nginx+Tomcat搭建服务集群,然后通过Spring Session+Redi ...

  7. spring boot集成redis实现session共享

    1.pom文件依赖 <!--spring boot 与redis应用基本环境配置 --> <dependency> <groupId>org.springframe ...

  8. springboot+spring session+redis+nginx实现session共享和负载均衡

    环境 centos7. jdk1.8.nginx.redis.springboot 1.5.8.RELEASE session共享 添加spring session和redis依赖 <depen ...

  9. SpringBoot学习笔记(13)----使用Spring Session+redis实现一个简单的集群

    session集群的解决方案: 1.扩展指定server 利用Servlet容器提供的插件功能,自定义HttpSession的创建和管理策略,并通过配置的方式替换掉默认的策略.缺点:耦合Tomcat/ ...

随机推荐

  1. 【第二周】关于java.util包下的Random类

    1.功能:此类的实例用于生成伪随机数流 2.方法(Random的方法有很多,在此只解释说明我认为比较常用的几个方法) (1)next(int bits):生成下一个伪随机数 (2)nextDouble ...

  2. SpringMVC 应知应会

    springMVC 是表现层技术,可以用来代替 struts2,下面是简略图:主要是处理器和视图,只有这两个部分需要编写代码. springMVC 三大组件:处理器映射器,处理器适配器,视图解析器. ...

  3. [LeetCode] Climbing Sairs

    You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb ...

  4. 【前端学习笔记】arguments相关

    arguments转数组: (function() { console.log(arguments instanceof Array); // --> false console.log(Obj ...

  5. 整合SSM框架应用

    普通方式 新建spring模块时引入如下内容: 启用devtools插件(热部署插件) idea需要做如下配置 settings-build-compiler->勾选build project ...

  6. FZU2121_神庙逃亡

    水题.直接解二次方程判断点的高度即可. #include <iostream> #include <cstring> #include <cstdio> #incl ...

  7. pycharm中新建并且运行django

    1.对于Bottle框架其本身未实现类似于Tornado自己基于socket实现Web服务,所以必须依赖WSGI,默认Bottle已经实现并且支持的WSGI有: 帮助我们写socket的server. ...

  8. 【WebAPI】新手入门WebAPI

    一.前言       工作也有一年多了,从进入公司就一直进行BIM(建筑信息模型)C/S产品的研发,平时写的最多的就是Dev WPF.一个偶然的时机,产品需要做支付宝扫码与微信扫码,所以需要了解产品服 ...

  9. BZOJ3771 Triple 【NTT + 容斥】

    题目链接 BZOJ3771 题解 做水题放松一下 先构造\(A_i\)为\(x\)指数的生成函数\(A(x)\) 再构造\(2A_i\)为指数的生成函数\(B(x)\) 再构造\(3A_i\)为指数的 ...

  10. BZOJ1069 [SCOI2007]最大土地面积 【凸包 + 旋转卡壳】

    题目链接 BZOJ1069 题解 首先四个点一定在凸包上 我们枚举对角线,剩下两个点分别是两侧最远的点 可以三分,复杂度\(O(n^2logn)\) 可以借鉴旋转卡壳的思想,那两个点随着对角线的一定单 ...