Redis+Cookie+Jackson+Filter原生实现 集群session共享问题

copy一份redis出来并分别启动服务端:
image.png

redis2的启动方式要以指定端口启动,或者conf修改了端口后,指定conf文件启动(即使在conf中设置了端口,以默认方式启动也是启动的6379端口):


redis-server
Jedis实现分布式客户端并添加数据:

在Redis在java客户端的使用--jedis中介绍了jedis的基本使用。分布式操作也差不多,不过这里使用的就不是JedisPool链接池了,而是使用的ShardedJedisPool
具体代码实现:

public class RedisShardedPool {
    private static ShardedJedisPool pool;//sharded jedis连接池
    private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getProperty("redis.max.total","20")); //最大连接数
    private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.max.idle","20"));//在jedispool中最大的idle状态(空闲的)的jedis实例的个数
    private static Integer minIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.min.idle","20"));//在jedispool中最小的idle状态(空闲的)的jedis实例的个数

    private static Boolean testOnBorrow = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.borrow","true"));//在borrow一个jedis实例的时候,是否要进行验证操作,如果赋值true。则得到的jedis实例肯定是可以用的。
    private static Boolean testOnReturn = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.return","true"));//在return一个jedis实例的时候,是否要进行验证操作,如果赋值true。则放回jedispool的jedis实例肯定是可以用的。

    private static String redis1Ip = PropertiesUtil.getProperty("redis1.ip");
    private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));
    private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");
    private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));




    private static void initPool(){
        JedisPoolConfig config = new JedisPoolConfig();

        config.setMaxTotal(maxTotal);
        config.setMaxIdle(maxIdle);
        config.setMinIdle(minIdle);

        config.setTestOnBorrow(testOnBorrow);
        config.setTestOnReturn(testOnReturn);

        config.setBlockWhenExhausted(true);//连接耗尽的时候,是否阻塞,false会抛出异常,true阻塞直到超时。默认为true。

        JedisShardInfo info1 = new JedisShardInfo(redis1Ip,redis1Port,1000*2);

        JedisShardInfo info2 = new JedisShardInfo(redis2Ip,redis2Port,1000*2);

        List jedisShardInfoList = new ArrayList(2);

        jedisShardInfoList.add(info1);
        jedisShardInfoList.add(info2);

        pool = new ShardedJedisPool(config,jedisShardInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN);
    }

    static{
        initPool();
    }

    public static ShardedJedis getJedis(){
        return pool.getResource();
    }


    public static void returnBrokenResource(ShardedJedis jedis){
        pool.returnBrokenResource(jedis);
    }



    public static void returnResource(ShardedJedis jedis){
        pool.returnResource(jedis);
    }
}
  • 设置config参数
  • 在JedisPool构造器中传入config,jedisShardInfoList...
  • 上述初始化完成过后就可以通过getJedis()获取到ShardedJedis对象,进行存储、查看、设置等操作,具体命令查看Redis基础命令
  • jedis操作后成功调用returnResource失败调用returnBrokenResources方法,这里真正的将之前的操作设置到了服务器。

注意:jedisShardInfoList分别添加了JedisShardInfo(info1,info2),连接两个redis服务器

添加filter更新token过期时间:

  • web.xml中添加filter:
    
        sessionExpireFilter
        com.mmall.controller.common.SessionExpireFilter
    
    
        sessionExpireFilter
        *.do
    

所有.do方法都会走这个过滤器。

  • 创建SessionExpireFilter实现Filter接口,在doFilter中执行更新token过期时间逻辑:
public class SessionExpireFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;

        String loginToken = CookieUtil.readLoginToken(httpServletRequest);

        if(StringUtils.isNotEmpty(loginToken)){
            //判断logintoken是否为空或者"";
            //如果不为空的话,符合条件,继续拿user信息

            String userJsonStr = RedisShardedPoolUtil.get(loginToken);
            User user = JsonUtil.string2Obj(userJsonStr,User.class);
            if(user != null){
                //如果user不为空,则重置session的时间,即调用expire命令
                RedisShardedPoolUtil.expire(loginToken, Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
            }
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }


    @Override
    public void destroy() {

    }
}

测试:

 public static void main(String[] args) {
        ShardedJedis jedis = RedisShardedPool.getResource();
        for(int i =0;i<10;i++){
            jedis.set("key"+i,"value"+i);
        }
        returnResource(jedis);
        System.out.println("program is end");
    }

查看服务端:


server

两个redis服务端都存入了数据

查看客户端:


client

看到6390中存入了key9、key7、key6、key4等数据,剩余的存入到6380中。

补充:上文完成了redis的分布实现,但在现实项目中如果需要项目中每个接口都能获取到存储的信息(比如:当前登录用户的信息),还需要对cookie进行设置:
public class CookieUtil {

    private final static String COOKIE_DOMAIN = ".zou.work";
    private final static String COOKIE_NAME = "login_token";


    public static String readLoginToken(HttpServletRequest request){
        Cookie[] cks = request.getCookies();
        if(cks != null){
            for(Cookie ck : cks){
                log.info("read cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());

                if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
                    log.info("return cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
                    return ck.getValue();
                }
            }
        }
        return null;
    }



    public static void writeLoginToken(HttpServletResponse response,String token){
        Cookie ck = new Cookie(COOKIE_NAME,token);
        ck.setDomain(COOKIE_DOMAIN);
        //代表设置在根目录
        ck.setPath("/");
        //防止脚本攻击
        ck.setHttpOnly(true);
        //单位是秒。
        //如果这个maxage不设置的话,cookie就不会写入硬盘,而是写在内存。只在当前页面有效。
        ck.setMaxAge(60 * 60 * 24 * 365);//如果是-1,代表永久
        log.info("write cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
        response.addCookie(ck);
    }


    public static void delLoginToken(HttpServletRequest request,HttpServletResponse response){
        Cookie[] cks = request.getCookies();
        if(cks != null){
            for(Cookie ck : cks){
                if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
                    ck.setDomain(COOKIE_DOMAIN);
                    ck.setPath("/");
                    ck.setMaxAge(0);//设置成0,代表删除此cookie。
                    log.info("del cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
                    response.addCookie(ck);
                    return;
                }
            }
        }
    }

当我们在登录时调用writeLoginToken,就将token存入到了cookie,通过浏览器可以看到存储成功,之后只要时在COOKIE_DOMAIN路径下的接口都可以获得该COKIE_NAME,这个时候在将COOKIE_NAME作为key,用户信息作为value存入到redis中就实现了--每个接口都能获取到存储的信息的效果(当然用户信息要先转化成字符串在存入,取的时候在转化成对应的bean即可(Jackson 封装):

image.png

你可能感兴趣的:(Redis+Cookie+Jackson+Filter原生实现 集群session共享问题)