25

延迟队列实现,定时任务,关闭订单

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzA3MTUzOTcxOQ%3D%3D&%3Bmid=2452975296&%3Bidx=2&%3Bsn=2aa888b60e93ac70f21932114567b030
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

场景

开发中经常需要用到定时任务,对于商城来说,定时任务尤其多,比如优惠券定时过期、订单定时关闭、微信支付2小时未支付关闭订单等等,都需要用到定时任务,但是定时任务本身有一个问题,一般来说我们都是通过定时轮询查询数据库来判断是否有任务需要执行,也就是说不管怎么样,我们需要先查询数据库,而且有些任务对时间准确要求比较高的,需要每秒查询一次,对于系统小倒是无所谓,如果系统本身就大而且数据也多的情况下,这就不大现实了,所以需要其他方式的,当然实现的方式有多种多样的,比如Redis实现定时队列、基于优先级队列的JDK延迟队列、时间轮等。因为我们项目中本身就使用到了Rabbitmq,所以基于方便开发和维护的原则,我们使用了Rabbitmq延迟队列来实现定时任务,不知道rabbitmq是什么的和不知道springboot怎么集成Rabbitmq的可以查看我之前的文章Spring boot集成RabbitMQ

1核2G,1年62元,3年200元,限量秒杀

想摸鱼好办啊,你可以买一台服务器,搭建个环境,部署个小应用啊,各种捣鼓一下,实战经验很重要,小投资大回报!

Rabbitmq延迟队列

Rabbitmq本身是没有延迟队列的,只能通过Rabbitmq本身队列的特性来实现,想要Rabbitmq实现延迟队列,需要使用Rabbitmq的死信交换机(Exchange)和消息的存活时间TTL(Time To Live)

死信交换机

一个消息在满足如下条件下,会进死信交换机,记住这里是交换机而不是队列,一个交换机可以对应很多队列。

一个消息被Consumer拒收了,并且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。

上面的消息的TTL到了,消息过期了。

队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上。

死信交换机就是普通的交换机,只是因为我们把过期的消息扔进去,所以叫死信交换机,并不是说死信交换机是某种特定的交换机

消息TTL(消息存活时间)

消息的TTL就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列没有消费者连着的保留时间,也可以对每一个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。如果队列设置了,消息也设置了,那么会取小的。所以一个消息如果被路由到不同的队列中,这个消息死亡的时间有可能不一样(不同的队列设置)。这里单讲单个消息的TTL,因为它才是实现延迟任务的关键。

可以通过设置消息的expiration字段或者x-message-ttl属性来设置时间,两者是一样的效果。只是expiration字段是字符串参数,所以要写个int类型的字符串:当上面的消息扔到队列中后,过了60秒,如果没有被消费,它就死了。不会被消费者消费到。这个消息后面的,没有“死掉”的消息对顶上来,被消费者消费。死信在队列中并不会被删除和释放,它会被统计到队列的消息数中去

处理流程图

Y3MBf2M.png!mobile

创建交换机(Exchanges)和队列(Queues)

创建死信交换机

3MbIna.png!mobile 如图所示,就是创建一个普通的交换机,这里为了方便区分,把交换机的名字取为:delay

创建自动过期消息队列

这个队列的主要作用是让消息定时过期的,比如我们需要2小时候关闭订单,我们就需要把消息放进这个队列里面,把消息过期时间设置为2小时 aqi6JnF.png!mobile 创建一个一个名为delay_queue1的自动过期的队列,当然图片上面的参数并不会让消息自动过期,因为我们并没有设置x-message-ttl参数,如果整个队列的消息有消息都是相同的,可以设置,这里为了灵活,所以并没有设置,另外两个参数x-dead-letter-exchange代表消息过期后,消息要进入的交换机,这里配置的是delay,也就是死信交换机,x-dead-letter-routing-key是配置消息过期后,进入死信交换机的routing-key,跟发送消息的routing-key一个道理,根据这个key将消息放入不同的队列

创建消息处理队列

这个队列才是真正处理消息的队列,所有进入这个队列的消息都会被处理 neANzu3.png!mobile 消息队列的名字为delay_queue2

消息队列绑定到交换机

进入交换机详情页面,将创建的2个队列(delay queue1和delay queue2)绑定到交换机上面 Mf2Afeb.png!mobile 自动过期消息队列的routing key 设置为delay

绑定delay queue2 63U73qV.png!mobile delay queue2 的key要设置为创建自动过期的队列的x-dead-letter-routing-key参数,这样当消息过期的时候就可以自动把消息放入delay_queue2这个队列中了

绑定后的管理页面如下图: mQjiqmj.png!mobile

当然这个绑定也可以使用代码来实现,只是为了直观表现,所以本文使用的管理平台来操作

发送消息

主要的代码就是

设置了让消息6秒后过期

注意:因为要让消息自动过期,所以一定不能设置delay_queue1的监听,不能让这个队列里面的消息被接受到,否则消息一旦被消费,就不存在过期了

接收消息

接收消息配置好delay_queue2的监听就好了

在消息监听中处理需要定时处理的任务就好了,因为Rabbitmq能发送消息,所以可以把任务特征码发过来,比如关闭订单就把订单id发过来,这样就避免了需要查询一下那些订单需要关闭而加重MySQL负担了,毕竟一旦订单量大的话,查询本身也是一件很费IO的事情

总结

基于Rabbitmq实现定时任务,就是将消息设置一个过期时间,放入一个没有读取的队列中,让消息过期后自动转入另外一个队列中,监控这个队列消息的监听处来处理定时任务具体的操作

阅读原文:   最新 3625页大厂面试题 


Recommend

  • 91

    Rabbitmq延迟队列实现定时任务

  • 9
    • www.extlight.com 3 years ago
    • Cache

    RabbitMQ 实现延迟队列

    最近开发一个活动功能,需要在活动结束后给榜单 Top10 用户发放奖励。由于活动的榜单是通过 RabbitMQ 进行异步统计分值排名的,因此在活动结束时队列中可能还存在消息未消费完全,排名不准确,此时发放活动奖励必然会出错。 那么,如果...

  • 2

    Go-Zero如何应对海量定时/延迟任务 mob604756f0bbf4 · 大约7小时之前 · 11 次点击 · 预计阅读时间 7 分钟 · 大约8小时之前 开始浏览    

  • 3
    • my.oschina.net 2 years ago
    • Cache

    高可用延迟队列设计与实现

    高可用延迟队列设计与实现 - kevwan的个人空间 - OSCHINA - 中文开源技术交流社区 <!-- 高可用延迟队列设计与实现 --> 延迟队列:一种带有 延迟功能 的消息队列 延时 → 未来一个不确定的时间 mq → 消费...

  • 8
    • segmentfault.com 2 years ago
    • Cache

    Golang 实现 RabbitMQ 的延迟队列

    读本文之前,你应该已经了解 RabbitMQ 的一些概念,如队列、交换机之类。延迟队列简介一个队列中的消息在延迟一段时间后才被消费者消费,这样的队列可以称之为延迟队列。延迟队列的应用场景十分广泛,如:下单...

  • 5

    延迟任务应用广泛,延迟任务典型应用场景有订单超时自动取消;支付回调重试。其中订单超时取消具有幂等性属性,无需考虑重复消费问题;支付回调重试需要考虑重复消费问题。 延迟任务具有如下特点:在未来的某个时间点执行;一...

  • 4

    “这是最好的时代,这是最坏的时代”,英国作家查尔斯·狄更斯在两百多年前写下的这句话,如果从辩证的角度来看,它或许可以适用于任何一个时代。我们生活在一个怎样的时代呢?我想,或许是一个矛盾的时代。因为,有时它让你对未来有无限的期待,...

  • 6

    百行代码实现基于Redis的可靠延迟队列 在之前探讨...

  • 4

    延迟队列在实际项目中有非常多的应用场景,最常见的比如订单未支付,超时取消订单,在创建订单的时候发送一条延迟消息,达到延迟时间之后消费者收到消息,如果订单没有支付的话,那么就取消订单。 那么,今天我们需要来谈的问题就是RabbitMQ、RocketMQ、Kafka...

  • 3

    在上一篇 Java 实现订单未支付超时自动取消,使用Java自带的定时任务TimeTask实现订单超时取消,但是有小伙伴提出这种实现,会有以下几个问题: ...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK