• 订单未支付30分钟自动取消是如何实现的?


    1.借助redis的过期特性
    下单时,订单状态是待支付。将订单编号作为key,下单的时间戳作为value,设置过期时间是30分钟。服务器监听redis的key过期事件,如果是订单过期(还会有其他key过期),则修改订单的状态为已取消。当30分钟后未支付则触发redis过期事件,只需修改订单状态即可。若30分钟内支付成功,则需要删除此订单在redis的值。当然,在支付时,需要检查订单是否已超时或已支付。很明确,只需要在应用中添加监听器监听redis过期即可。首先是配置redis监听器,代码如下所示:

    @Configuration
    public class RedisListenerConfig {
    
        @Bean
        RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            return container;
        }
    
    }

    继承redis键过期监听器,进行业务处理

    @Component
    public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {


    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
    super(listenerContainer);
    }


    @Override
    public void onMessage(Message message, byte[] pattern) {
    // message.toString()可获取失效的key
    String expiredKey = message.toString();
    System.out.println("expiredKey:" + expiredKey);
    if (expiredKey.startsWith("order")) {
    // 获取订单orderNo
    String orderNo = expiredKey.substring(expiredKey.lastIndexOf(":") + 1);
    // TODO 处理过期的订单 将待支付的订单改为已取消(超时未支付)
    System.out.println("orderNo:"+orderNo);

    }
    }


    }

    注意:由于存在多个键的过期,故必须对键进行判断,是否是订单超时造成的过期。通过开启key过期的事件通知,当key过期时,会发布过期事件;我们定义key过期事件的监听器,当key过期时,就能收到回调通知。
    1)由于Redis key过期删除是定时+惰性,当key过多时,删除会有延迟,回调通知同样会有延迟。因此性能较低
    2)且通知是一次性的,没有ack机制,若收到通知后处理失败,将不再收到通知。需自行保证收到通知后处理成功。
    3)通知只能拿到key,拿不到value
    4)Redis将数据存储在内存中,如果遇到恶意下单或者刷单的将会给内存带来巨大压力
    2、JDK的延迟队列
    该方案是利用JDK自带的DelayQueue来实现,这是一个无界阻塞队列,该队列只有在延迟期满的时候才能从中获取元素,放入DelayQueue中的对象,是必须实现Delayed接口的。
    DelayedQueue实现工作流程如下图所示

    Poll():获取并移除队列的超时元素,没有则返回空。
    take():获取并移除队列的超时元素,如果没有则wait当前线程,直到有元素满足超时条件,返回结果。
    定义一个类OrderDelay实现Delayed,代码如下

    public class OrderDelay implements Delayed {
    
        private String orderId;
    
        private long timeout;
    
        OrderDelay(String orderId, long timeout) {
            this.orderId = orderId;
            this.timeout = timeout + System.nanoTime();
        }
    
        @Override
        public int compareTo(Delayed other) {
            if (other == this) {
                return 0;
            }
            OrderDelay t = (OrderDelay) other;
            long d = (getDelay(TimeUnit.NANOSECONDS) - t.getDelay(TimeUnit.NANOSECONDS));
            return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
        }
    
        /**
         * 返回距离你自定义的超时时间还有多少
         */
        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(timeout - System.nanoTime(), TimeUnit.NANOSECONDS);
        }
    
        void print() {
            System.out.println(orderId + "编号的订单要删除啦。。。。");
        }
    
    
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("00000001");
            list.add("00000002");
            list.add("00000003");
            list.add("00000004");
            list.add("00000005");
            DelayQueue<OrderDelay> queue = new DelayQueue<OrderDelay>();
            long start = System.currentTimeMillis();
            for(int i = 0; i < 5; i++){
                //延迟三秒取出
                queue.put(new OrderDelay(list.get(i), TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS)));
                try {
                    queue.take().print();
                    System.out.println("After " + (System.currentTimeMillis()-start) + " MilliSeconds");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
    }

    输出如下

    00000001编号的订单要删除啦。。。。
    After 3005 MilliSeconds
    00000002编号的订单要删除啦。。。。
    After 6010 MilliSeconds
    00000003编号的订单要删除啦。。。。
    After 9011 MilliSeconds
    00000004编号的订单要删除啦。。。。
    After 12015 MilliSeconds
    00000005编号的订单要删除啦。。。。
    After 15018 MilliSeconds

    可以看到都是延迟3秒,订单被删除
    优缺点
    优点:效率高,任务触发时间延迟低。
    缺点:(1)服务器重启后,数据全部消失,怕宕机
    (2)集群扩展相当麻烦
    (3)因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现OOM异常
    (4)代码复杂度较高

    参考文档
    https://blog.csdn.net/jike11231/article/details/125278050
    https://www.cnblogs.com/zys2019/p/16366349.html

  • 相关阅读:
    Redis Cluter
    数据库设计范式
    kvm虚拟化
    架构前端
    集群架构
    初识shell编程
    网络知识
    Linux三剑客
    Linux磁盘管理
    高性能异步爬虫
  • 原文地址:https://www.cnblogs.com/jelly12345/p/16591074.html
Copyright © 2020-2023  润新知