自动取消已放弃的订单
自动取消过期订单
今天我们来看另一个需要借助消息队列异步处理的业务场景 —— 自动取消已经放弃的订单。
在网上购物商城,我们需要为已经下单的实体商品订单(尤其是限量的抢购订单,或者需要依赖商家及时响应的外卖订单)设置支付有效期,以免用户下单后长时间不支付,或者根本不打算支付,而又没有手动取消,白白占用了库存额度,让其他想买的用户无法购买。
有了支付有效期后(通常是 15 分钟、半小时或者一小时),超过这个有效期,我们就认为用户放弃这个订单了,然后系统会自动取消这个订单,释放对应库存。显然,这可以通过 Laravel 消息队列提供的延迟分发功能来实现。
延迟处理任务
假设这个监控订单状态并在过期后将其取消的队列任务类是 MonitorPendingOrder
,对应的订单取消操作定义在 handle
方法中:
我们可以在分发这个队列任务时通过 delay
指定延迟时间:
这样一来,该任务就会在一小时后才会被推送到消息队列进行后续处理。
取消前发送提醒
当然,如果是用户真的忘记支付了,在取消前发送提醒消息会更友好一些,我们将订单监控任务类的 handle
处理代码调整如下:
这里使用了
release
延迟推送任务,其功能和delay
等效。
然后每隔十五分钟给用户发送提醒,直到订单被取消:
确保队列任务执行了足够多的次数
前面我们已经说过,每次任务被推送到消息队列执行被都被看作尝试了一次,因此,按照上面这个逻辑,MonitorPendingOrder
这个任务类会被执行四次,如果配置的 tries
属性值小于 4 的话,那么订单将无法被取消,因为后续试图推送这个任务时,会被认为已经达到尝试次数上限而被丢弃,所以至少要将其最大尝试次数配置为 4:
队列任务延迟注意事项
另外一个需要注意的细节是,上面的延迟推送控制的时间点是任务推送到消息队列的时间,而任务真正执行的时间点是不可控的,如果队列很空闲,则其执行的时间点基本也能做到与预期相符,但如果队列负载很高,那么就要等到前面的所有任务处理完成后才能执行它,如果碰巧前面的任务是一个耗时任务,可能要等待几分钟甚至数小时,就与预期相差太多,到了不能接受的地步了,这个时候,可以运行多个队列处理器进程来尽可能快地提高队列任务处理速度,并且将这个任务的优先级调到更高。
3 Comments
之前用的是有序集合,按照取消的时间戳进行排序,然后进行轮训扫描,到时间了直接取消处理
延迟任务不是,60分钟后才会从有序集合中轮询到数据,然后执行job dispatch吗,这4次是怎么出发的,不是只是到了60分钟后执行一次handle吗
dispatch是15分钟,然后到第15分钟handle的时候再往后(release)延迟15分钟,总共4次; 然后在第四次的时候前面有个判断条件,就是订单时间超过59分钟,就直接取消。在第四次的时候就不往下执行了