• 关于Redis的事件回调解析


    基本概念

    Redis的过期回调可以实现我们的redsi的key在过期的时候回调一些接口从而来实现项目中需要的一些功能。比如我们想在订单超时的时候进行关闭,可以用这个来进行一个简单的实现,当然实际的项目中能否这样使用我们暂且不做讨论,这里只是举个例子。

    Redis的事件回调的特点

    • Redis的过期回调底层是根据Redis的发布订阅实现的,通过订阅特定的频道,实现某些节点过期了之后向特定的频道发送消息,触发回调的功能。
    • 不能保证可靠性,因为Redis的事件回调是基于发布订阅实现的,而发布订阅是发送即忘,所以如果我们项目中需要可靠的回调,就不能使用这种方式。当订阅事件的客户端断线时, 它会丢失所有在断线期间分发给它的事件。另外redis的过期也并不是保证能够实时过期,2.4之前过期时间的误差官方文档说的是0-1秒,2.4之后是0-1毫秒。
    • Redis的事件回调不仅仅是过期回调,可以是很多其他的命令的回调,这个部分可以参考
    • 所有命令都只在键真的被改动了之后,才会产生通知。例如试图删除不存在于集合的元素时,删除操作会执行失败,因为没有真正的改动键,所以这一操作不会发送通知。

    Redis是怎么实现过期的?

    redis的过期是根据上次更新时间和当前时间的差值,比较看是否大于设置的过期时间,大于就过期。但是redis服务宕机了之后设置了过期时间的key如果还没有过期,就会导致过期时间失效,变成永久的key。
    另外要注意,redis过期的key不会立马删除,因为redis采用定时删除和惰性删除,有可能到了过期时间但是key还没有删除,这个时候我redis宕机了,重启之后即使是已经过期了的key也会变成永久的。

    过期事件回调的应用实战

    首先打开redis的配置文件,然后将notify-keyspace-events Ex这个配置的注释打开,由于打开这个回调的通知会对redis的性能有一定的影响,所以官方默认是关闭的。这个Ex参数的作用就是生命是过期事件才进行发布通知。

    然后引入依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
                <version>2.1.9.RELEASE</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建配置类

    package com.dongmu.config;
    
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.listener.ChannelTopic;
    import org.springframework.data.redis.listener.RedisMessageListenerContainer;
    
    @Configuration
    public class RedisConfiguration {
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
    
        @Bean
        public ChannelTopic expireTopic(){
            return new ChannelTopic("test01");
        }
    
        @Bean
        public RedisMessageListenerContainer redisMessageListenerContainer(){
            RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
            redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
            return redisMessageListenerContainer;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    这里说明一下为什么需要这个配置类RedisMessageListenerContainer ,这个类在我们下面创建一个监听器类的时候需要用到这个对象作为入参,所以必须创建,不然由于构造函数参数缺少没无法创建监听器bean对象。

    创建监听器bean对象

    package com.dongmu.listener;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.connection.Message;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
    import org.springframework.data.redis.listener.RedisMessageListenerContainer;
    import org.springframework.stereotype.Component;
    
    @Component
    public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
        public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
            super(listenerContainer);
        }
    
    
        @Autowired
        StringRedisTemplate stringRedisTemplate;
    
        @Override
        public void onMessage(Message message,byte[] pattern){
            String expireKey = message.toString();
            System.out.println(expireKey);
    
            if (expireKey.startsWith("dongmu:test")){
                System.out.println(expireKey+"过期了");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    onMessage方法里面可以处理我们的逻辑。

    下面编写一个Controller进行测试

    @Autowired
        StringRedisTemplate redisTemplate;
        @RequestMapping("/redis/test")
        public void RedisTest(){
            redisTemplate.opsForValue().set("dongmu:test:key1","mytestkey1",5, TimeUnit.SECONDS);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    访问接口
    在这里插入图片描述
    结果
    在这里插入图片描述
    可以发现这里的过期通知生效了。

    redis的过期回调实现订单的超时关闭

    redis的过期回调并不能够保存可靠性,所以一般不能这样用,如果这样用的话还要定时扫描数据库处理已经超时的订单,不能完全依靠redis的过期回调通知,另外redis 的过期时间有可能变成永久的,而且开启了过期回调会影响redis的性能,所以尽量不要这样用,小项目的话可以尝试以下,大项目的或者要求比较高的场景一定要使用专业的消息中间件。

    docker中配置redis

    一般我们的linux服务器上的redis可能会安装在docker容器内部,这个时候想要修改容器中redis的配置就需要做挂载,步骤如下:

    docker pull redis #下载最新版本的redis
    #下面这一步会把我们的内部的文件夹挂载到外部文件夹以及映射的端口,
    #要注意这个命令里面指定了配置文件
    docker run -p 9999:6379 --name redis -v /usr/local/docker:/etc/redis -v /usr/local/docker/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes
    
    #然后我们利用下载最新的redsi的配置文件,在配置文件中指定redis的密码,
    #注意这里配置文件一定要用和这个版本一致的配置文件不然还会报错
    requirepass ***(这里写你的密码)
    maxclients 1000(最大的连接数量)
    
    #同时把过期回调的注释打开
    notify-keyspace-events Ex
    
    #配置好了之后我们启动docker 中的redis容器即可
    dockers start 容器id
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    亲测服务器上过期回调成功
    在这里插入图片描述

    在这里插入图片描述
    对于已经启动的redis我们还可以查看docker的配置

    docker inspect redis #即可查看
    
    • 1
  • 相关阅读:
    Linux基本指令(下)
    Spring Data MongoTemplate insert 插入数据报duplicate key的问题
    代码随想录1刷—二叉树篇(一)
    Nginx正则表达与Rewrite跳转
    推荐系统方法梳理
    【设计模式】适配器模式:攻敌三分,自留七分,以超兽武装的例子来谈谈适配器模式
    【node.js】第六章 初识express
    【Unity3D】绘制物体表面三角形网格
    数据库:Hive转Presto(四)
    MR案例 - 计算总成绩
  • 原文地址:https://blog.csdn.net/qq_45401910/article/details/126754549