Redis实现key过期监听,并操作redis的多个数据库,集成到SpringBoot

最近来了个新的需求,需要使用定时器完成,本想以为用个@Scheduled()就轻易搞定的,详细了解后,事情却并没有这么简单......。所以接到需求后,需要找产品明确明确再次明确,才开工,不然的话你本以为做好的工作却是一场空。

业务场景逻辑解析:第一个请求进来,需要把请求参数暂时保存下来,并触发一个定时器。如果第二个请求在定时器未过期期间进来,去拿第一次请求的参数和第二次请求的参数进行对比,选出一个更合理的参数进行操作。如果第二个请求在定时器过期时还未过来,则过期触发取第一次请求的参数来执行操作。如果在定时器过期后,第二个请求才过来,还是需要拿第一次参数和第二次参数进行对比,若第二次参数更合理,则再次执行。

业务与技术相结合:因为第一次请求参数只是暂时保存下来,自然而然就想到放到缓存里。把第一次请求的参数保存在redis的0号库和1号库里头,并且以一定的规则命名key值,使得两次请求的key值是一样的,0号库设置过期时间为30S,1号库则设置过期时间为1天。启用0号库的key过期监听,每次请求或者过期触发,都去比较参数,若执行则加一个执行标识在value里,这样就可以完成以上的业务场景。

以上只是一时兴起,回忆一下做的功能,下面进入主题。

一、Redis启动key过期监听,基于spring

redis可以实现类似于消息中间件的一个发布/订阅的功能,在spring里发布和订阅的方式有多种方式,我只展示其中的一种(key过期)。

①发布key过期消息通道

key的过期监听,其实就是发布了一个消息通道,一旦有key过期了,就会把该key推送出去。当然,在该通道发布之前,我们就必须进行订阅。

这个key过期发布的通道,redis有提供一个专门打开的开关,在redis.conf里进行配置:

(当然,我们也可以自己发布其他消息通道)

默认是不打开(打开对CUP有消耗):

notify-keyspace-events ""

打开后:

notify-keyspace-events "Ex"

然后重启redis服务,就可以生效。

打开这个key过期发布的消息通道后,我们就编写程序该通道进行订阅。

*redis.conf是在redis安装目录下

②订阅key过期消息通道

基于springBoot,一启动就进行订阅key过期消息通道,新建一个类,并注解@Component,并且实现CommandLineRunner接口,这样当spring容器加载完之后,就会马上执行该组件

package com.appscomm.device.common;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;



@Component
public class RedisSubscribeThread implements CommandLineRunner {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void run(String... strings) throws Exception {
        redisTemplate.execute(new RedisCallback() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.pSubscribe(new MessageListener() {
                    @Override
                    public void onMessage(Message message, byte[] pattern) {

                     System.out.println("message:"+message);                

                    //业务处理


                    }
                },"*@0*".getBytes());
                return null;
            }
        });
    }

}

稍作解析:

1.@Component,spring的组件注解,会加入到bean容器

2.CommandLineRunner,spring容器启动完后进行加载

3."*@0*".getBytes(),监听redis0号库的所有发布消息(因为只有1一个地方用到,所以只有一个)

自此,key过期订阅已经实现。测试:

redisTemplate.opsForValue().set("keyTest","123",5,TimeUnit.SECONDS);

默认插入是redis的0号库

5秒钟后,key过期的打印结果:

二、操作redis的多个数据库

基于spring,切换redis的数据库进行存储和获取:

通过redisTemplate来进行获取JedisConnectionFactory 连接工厂进行切换。

        JedisConnectionFactory factory =(JedisConnectionFactory)redisTemplate.getConnectionFactory();
        factory.setDatabase(0);
        JedisConnection jedisConnectionZero=factory.getConnection(); //获取redis的0号库连接
        factory.setDatabase(1);
        JedisConnection jedisConnectionOne=factory.getConnection();  //获取redis的1号库连接

        try {

        jedisConnectionZero.setEx("testKey0".getBytes(),30,"aaa".getBytes()); //存到redis的0号库
        jedisConnectionOne.setEx("testKey1".getBytes(),50,"bbb".getBytes());//存到redis的1号库

        String testKey0Value=new String(jedisConnectionZero.get("testKey0".getBytes())); //从redis的0号库取出
        String testKey1Value=new String(jedisConnectionOne.get("testKey1".getBytes()));  //从redis的1号库取出

        System.out.println("0号库的key为testKey0对应的Value值:"+testKey0Value);
        System.out.println("1号库的key为testKey1对应的Value值:"+testKey1Value);

        }catch (Exception e){
            System.out.println("redis存取错误!")
        }finally {
            if(jedisConnectionZero!=null){
                jedisConnectionZero.close();
            }
            if(jedisConnectionOne!=null){
                jedisConnectionOne.close();
            }
        }


        

稍作解析:

redisTemplate获取JedisConnectionFactory 工厂对象,再获取jedis连接,每次切换数据库(factory.setDatabase(数据库号)),都需要重新获取jedis的连接

最后记得把jedis的连接关闭!!!

测试结果:

自此,redis切换数据库进行操作完成,如有错漏,请大家指正。

猜你喜欢

转载自blog.csdn.net/Ouyzc/article/details/82752303