建议不要使用,redis过期监听实现关闭订单

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了建议不要使用,redis过期监听实现关闭订单相关的知识,希望对你有一定的参考价值。

参考技术A

在电商、支付等领域,往往会有这样的场景,用户下单后放弃支付了,那这笔订单会在指定的时间段后进行关闭操作,细心的你一定发现了像某宝、某东都有这样的逻辑,而且时间很准确,误差在1s内;那他们是怎么实现的呢?

一般实现的方法有几种:

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

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

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

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

有另一位大佬做了测试 请勿过度依赖Redis的过期监听, 有兴趣的朋友可以自行查阅。

死信(Dead Letter) 是 rabbitmq 提供的一种机制。当一条消息满足下列条件之一那么它会成为死信:

若配置了死信队列,死信会被 rabbitmq 投到死信队列中。

在 rabbitmq 中创建死信队列的操作流程大概是:

死信队列的设计目的是为了存储没有被正常消费的消息,便于排查和重新投递。 死信队列同样也没有对投递时间做出保证,在第一条消息成为死信之前,后面的消息即使过期也不会投递为死信

为了解决这个问题,rabbit 官方推出了延迟投递插件 rabbitmq-delayed-message-exchange ,推荐使用官方插件来做延时消息。

时间轮是一种很优秀的定时任务的数据结构,然而绝大多数时间轮实现是纯内存没有持久化的。运行时间轮的进程崩溃之后其中所有的任务都会灰飞烟灭,所以奉劝各位勇士谨慎使用。

redisson delayqueue 是一种基于 redis zset 结构的延时队列实现。delayqueue 中有一个名为 timeoutSetName 的有序集合,其中元素的 score 为投递时间戳。delayqueue 会定时使用 zrangebyscore 扫描已到投递时间的消息,然后把它们移动到就绪消息列表中。

delayqueue 保证 redis 不崩溃的情况下不会丢失消息,在没有更好的解决方案时不妨一试。

在数据库索引设计良好的情况下,定时扫描数据库中未完成的订单产生的开销并没有想象中那么大。在使用 redisson delayqueue 等定时任务中间件时可以同时使用扫描数据库的方法作为补偿机制,避免中间件故障造成任务丢失。

文章来自https://www.cnblogs.com/Finley/p/16395466.html

SpringBoot 监听Redis key过期回调

场景

Spring boot实现监听Redis key失效事件可应对某些场景例如:处理订单过期自动取消、用户会员到期…

开启Redis键过期回调通知

Redis默认是没有开启键过期监听功能的,需要手动在配置文件中修改。

  • Linux操作系统
    • 修改redis安装目录下的redis.conf配置文件,然后找到notify-keyspace-events Ex这行代码,默认是注释掉的,取消注释即可(即删除掉前面的#)。
    • 也有一种情况是没有这一行的,这种情况下直接把这一行添加上去即可。
    • 然后重启redis
  • Windows操作系统
    • 在安装目录下找到redis.windows.confredis.windows-service.conf两个文件,然后分别修改这两个文件中的notify-keyspace-events Ex字段,取消注释即可。
    • 然后重启redisWindows重启命令为redis-server.exe --service-start

Java代码实现监听回调

添加Redis键过期监听配置

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;
   
   

创建监听类

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;

@Component
public class KeyExpiredListener extends KeyExpirationEventMessageListener 

    public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) 
        super(listenerContainer);
    

    /**
     * 使用该方法监听,当Redis的key失效的时候执行该方法
     * @param message message must not be @literal null.
     * @param pattern pattern matching the channel (if specified) - can be @literal null.
     */
    @Override
    public void onMessage(Message message, byte[] pattern) 
        // 过期的Key
        String expiraKey = message.toString();
        System.out.println("该Key已失效:"+expiraKey);
    


指定Rediskey 设置值及其过期时间

Redis Setex 命令为指定的 key 设置值及其过期时间。如果 key 已经存在, SETEX 命令将会替换旧的值。

以上是关于建议不要使用,redis过期监听实现关闭订单的主要内容,如果未能解决你的问题,请参考以下文章

领导:谁再用 Redis 过期监听实现关闭订单,立马滚蛋!

Spring boot实现监听Redis key过期事件

Redis过期事件通知实现订单自动关闭

CTO 说了:谁再用 Redis 过期监听实现定时任务,立马滚蛋!

CTO 说了:谁再用 Redis 过期监听实现定时任务,立马滚蛋!

Spring+Redis(keyspace notification)实现定时任务(订单过期自动关闭)