• 利用 Redis 也能实现订单30分钟自动取消?


    利用 Redis 也能实现订单30分钟自动取消

    业务场景

    我们以订单功能为例说明下:

    生成订单后一段时间不支付订单会自动关闭。最简单的想法是设置定时任务轮询,但是每个订单的创建时间不一样,定时任务的规则无法设定,如果将定时任务执行的间隔设置的过短,太影响效率。

    还有一种想法,在用户进入订单界面的时候,判断时间执行相关操作。方式可能有很多,在这里介绍一种监听 Redis 键值对过期时间来实现订单自动关闭。

    实现思路

    在生成订单时,向 Redis 中增加一个 KV 键值对,K 为订单号,保证通过 K 能定位到数据库中的某个订单即可,V 可为任意值。

    假设,生成订单时向 Redis 中存放 K 为订单号,V 也为订单号的键值对,并设置过期时间为 30 分钟,如果该键值对在 30 分钟过期后能够发送给程序一个通知,或者执行一个方法,那么即可解决订单关闭问题。

    实现:通过监听 Redis 提供的过期队列来实现,监听过期队列后,如果 Redis 中某一个 KV 键值对过期了,那么将向监听者发送消息,监听者可以获取到该键值对的 K,注意,是获取不到 V 的,因为已经过期了,这就是上面所提到的,为什么要保证能通过 K 来定位到订单,而 V 为任意值即可。拿到 K 后,通过 K 定位订单,并判断其状态,如果是未支付,更新为关闭,或者取消状态即可。

    开启 Redis key 过期提醒

    修改 redis 相关事件配置。找到 redis 配置文件 redis.conf,查看 notify-keyspace-events 配置项,如果没有,添加 notify-keyspace-events Ex,如果有值,则追加 Ex,相关参数说明如下:

    • K:keyspace 事件,事件以 keyspace@ 为前缀进行发布
    • E:keyevent 事件,事件以 keyevent@ 为前缀进行发布
    • g:一般性的,非特定类型的命令,比如del,expire,rename等
    • $:字符串特定命令
    • l:列表特定命令
    • s:集合特定命令
    • h:哈希特定命令
    • z:有序集合特定命令
    • x:过期事件,当某个键过期并删除时会产生该事件
    • e:驱逐事件,当某个键因 maxmemore 策略而被删除时,产生该事件
    • A:g$lshzxe的别名,因此”AKE”意味着所有事件

    引入依赖

    在 pom.xml 中添加 org.springframework.boot:spring-boot-starter-data-redis 依赖

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

    相关配置

    定义配置 RedisListenerConfig 实现监听 Redis key 过期时间

    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.RedisMessageListenerContainer;
    
    @Configuration
    public class RedisListenerConfig {
    
        @Bean
        RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
    
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            return container;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    定义监听器 RedisKeyExpirationListener,实现KeyExpirationEventMessageListener 接口,查看源码发现,该接口监听所有 db 的过期事件 keyevent@*:expired"

    import org.springframework.data.redis.connection.Message;
    import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
    import org.springframework.data.redis.listener.RedisMessageListenerContainer;
    import org.springframework.stereotype.Component;
    
    /**
     * 监听所有db的过期事件__keyevent@*__:expired"
     */
    @Component
    public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
    
        public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
            super(listenerContainer);
        }
    
        /**
         * 针对 redis 数据失效事件,进行数据处理
         * @param message
         * @param pattern
         */
        @Override
        public void onMessage(Message message, byte[] pattern) {
    
            // 获取到失效的 key,进行取消订单业务处理
            String expiredKey = message.toString();
            System.out.println(expiredKey);
        }
    }
    
    • 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

    文章来源: https://mp.weixin.qq.com/s/JUI0kXwQww7UCizz0YF5bw

    redis 过期监听真的好么?

    在 Redis 官方手册的keyspace-notifications: timing-of-expired-events中明确指出:

    Basically expired events are generated when the Redis server deletes the key and not when the time to live theoretically reaches the value of zero

    redis 自动过期的实现方式是:定时任务离线扫描并删除部分过期键;在访问键时惰性检查是否过期并删除过期键。redis 从未保证会在设定的过期时间立即删除并发送过期通知。实际上,过期通知晚于设定的过期时间数分钟的情况也比较常见。

    此外键空间通知采用的是发送即忘(fire and forget)策略,并不像消息队列一样保证送达。当订阅事件的客户端会丢失所有在断线期间所有分发给它的事件。

    这是一种比定时扫描数据库更 “LOW” 的解决方案,不建议使用。

    实现关闭订单的方法

    一般实现的方法有几种:

    • 使用 rocketmq、rabbitmq、pulsar 等消息队列的延时投递功能
    • 使用 redisson 提供的 DelayedQueue

    有一些方案虽然广为流传但存在着致命缺陷,不要用来实现延时任务

    • 使用 redis 的过期监听
    • 使用 rabbitmq 的死信队列
    • 使用非持久化的时间轮

    文章参考:https://mp.weixin.qq.com/s/T5jw9ZXOL1MMkMdmIyhQ6Q

  • 相关阅读:
    数据库设计中如何选择主键 (自然键或代理键 )| Part 1
    nodejs:fs文件系统模块
    vue前端使用echart径向树形图修改样式
    【Java SE】类和对象
    Python 算法:感受算法的小小魅力和复杂度的计算
    【C++】深拷贝和浅拷贝 ④ ( 深拷贝示例 )
    9.25 校招 实习 内推 面经
    VS2010 组件列表与对应名称
    Orleans 微软基于 Actor 的分布式框架
    中国传统美食网页HTML代码 学生网页课程设计期末作业下载 美食大学生网页设计制作成品下载 DW餐饮美食网页作业代码下载
  • 原文地址:https://blog.csdn.net/weixin_46237429/article/details/125477984