redis session共享总结

986 阅读3分钟

前言

session是一个由tomcat管理的对象,初次访问tomcat时产生,同时产生的,还有一个唯一的sessionId。
以后只要带着这个sessionId访问tomcat,就会被判定为 “一个会话”,不会重复创建session。
session默认保存在内存里,每个tomcat,各自保管各自的。

用户与session的联系,一般体现在登录后,或者其它场景下查了某些值,然后set到session里。单机应用按照这个模式可以很愉快的运行,主要是集群。

他很烦ヽ(ー_ー)ノ

tomcat集群不会单独出现,得绑定一个nginx,靠nginx自动转发请求才能正常运行,而出于负载均衡的考虑,请求不会每次都落在同一个tomcat上。
因此如果不共享session,那么当请求落在其它tomcat上时,就会被判定为一个新的会话,创建一个新的session。

所以要怎么办呢?

一.tomcat自带的redis session共享

之前讲过,tomcat默认把session保存在内存里,那么只要修改一下配置,让tomcat把session保存到redis,并且读也从redis读,session共享就简单实现了。

这个过程分为两步:

准备jar包

  • jedis-2.9.0.jar
  • commons-pool2-2.9.0.jar
  • tomcat-redis-session-manager-2.0.0.jar

下载完毕后,放到tomcat的lib目录即可,jar包的版本我没有仔细研究过,上面罗列的是一个可行版本组合。

起主要作用的,是tomcat-redis-session-manager.jar,这里可以猜想一下他的原理。tomcat应该是把获取session以及保存session做在一个接口内。比如这样:

interface SessionManager {
    Session getSession();
    void saveSession();
}

tomcat-redis-session-manager这个jar包再去实现这个接口,获取session从redis拿,保存session保存到redis内。当然我这两句话肯定讲的特别宽泛,真实场景不会这么简单,不然也不会需要commons-pool2-2.9.0.jar这个对象池。

jar包到位后,就可以开始配置了。

修改context.xml

<Context>
    <!-- Default set of monitored resources -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->
    <!-- Uncomment this to enable Comet connection tacking (provides events
         on session expiration as well as webapp lifecycle) -->
    <!--
    <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
    -->
	<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
	<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
	   host="127.0.0.1"
	   port="6379"
	   database="0"
	   maxInactiveInterval="60" />
</Context>

在context.xml中,插入两个节点,valve与manager,然后重启tomcat,session就被共享了。

  • valve 刚看见这个节点以为是value,仔细看了看才确认是"valve",又好好的盯了一会才发现居然真是那个“Valve”。而且valve背后的机制,作用还不小,详情见这篇博客(www.cnblogs.com/benwu/artic…)
  • manager 配置redis的连接信息,对redis的读写也都靠这个类完成。

二.免配置的springboot springsession

直接通过tomcat去做session共享还是有点复杂的,而且实际开发中,一般不由程序员处理,都是运维直接搞定。如今springboot大行其道,自然有更加简单的方式。

首先新建两个SpringBoot项目

image.png

然后在pom.xml里准备两个denpendency

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

最后在application.properties里放redis的连接参数以及session存储方式

spring.redis.host=localhost
spring.redis.port=6379

spring.session.store-type=redis

结束了!

  • 原理推测 SpringBoot是内置tomcat的,因此session共享的过程和我们上一章节讲的相差不会很大。

    引入spring-boot-starter-data-redis这个denpendency,就好比引入jedis.jar,对redis进行增删查改。引入spring-session-data-redis,好比引入tomcat-redis-session-manager.jar,自定义session的存储以及读取方式。

三.某些简单场景

上面两节讲的,都是把整个session对象保存到redis中,但是看场景,有时候,我们只需要往redis里保存一些特定的key,像用户信息、菜单。

比方说,只有两个tomcat节点,session的作用也只是判断一下用户有没有登录过,是否登录过期。
这种情况下,约定好一个登录的Key,登录成功后,保存到redis中,每次访问时,在拦截器里校验这个key是否存在即可。
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate redisTemplate;

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (request.getRequestURI().contains("/login") || request.getRequestURI().contains("/error")) {
            return true;
        }

        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        Object attribute = operations.get(HelloController.LOGIN_KEY);

        if (attribute ==null || StringUtils.isBlank(attribute.toString())) {
            response.sendRedirect(request.getContextPath() + "/login");
            return false;
        }

        return true;
    }
}