zoukankan      html  css  js  c++  java
  • Spring Session加Redis

    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中出现如下的内容:

    image

    先不解释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集合中删除掉。

  • 相关阅读:
    【leetcode】1365. How Many Numbers Are Smaller Than the Current Number
    【leetcode】1363. Largest Multiple of Three
    【leetcode】1362. Closest Divisors
    【leetcode】1361. Validate Binary Tree Nodes
    【leetcode】1360. Number of Days Between Two Dates
    【leetcode】1359. Count All Valid Pickup and Delivery Options
    【leetcode】1357. Apply Discount Every n Orders
    【leetcode】1356. Sort Integers by The Number of 1 Bits
    ISE应用入门的一些问题
    DDR的型号问题
  • 原文地址:https://www.cnblogs.com/kkdn/p/9674341.html
Copyright © 2011-2022 走看看