通过事件监听器异步发放优惠券


事件与事件监听器

在基于事件驱动程序构建的应用中,一个事件可能有多个事件监听器,以 NewOrderSubmitted 事件为例,会被更新顾客订单、发送账单、发送优惠券等监听器监听:

当我们在控制器中触发 NewOrderSubmitted 事件时:

Laravel 就会调用所有监听器的 handle 方法进行相应的处理。

UpdateCustomerMetrics 监听器用于更新用户订单数量以及一些其他的指标数据,我们希望这个监听器可以在事件触发后立即执行,这样一来,当用户查看后台数据时可以看到更新后的数据。

另一方面,用于发送账单的 SendInvoice 监听器以及用于通过发送优惠券奖励老用户的 SendGiftCoupon 监听器可以晚一点执行。我们不想让用户等到这两个邮件发送后才能重定向到 /thanks 页面。

将监听器分发到队列

要实现这样的功能,可以将监听器推送到消息队列异步执行:

通过在监听器类中实现 ShouldQueue 接口,我们可以告知 Laravel 将这个监听器推送到队列,而不是直接调用 handle 方法。

在基于队列驱动的监听器中,可以配置任务尝试次数、backoff、超时、重试条件、延迟时间、连接和队列名称等属性:

还可以定义 failed 方法在监听器任务执行失败时调用:

底层实现细节可以参考学院君之前发布的基于 Redis 消息队列实现 Laravel 事件监听及底层源码探究这篇教程。

按条件发送优惠券

对于每个新订单,都需要发送账单邮件,但是不是每个订单都有优惠券奖励,通常会有一个积分系统判定用户在每次购买后是否可以获取优惠券奖励。

这里是用于发送优惠券的 SendGiftCoupon 监听器类的 handle 方法代码:

每次任务运行时都会检查用户是否有资格获取奖励。

真正的挑战来了:优惠券资格可能在订单创建时和任务运行时并不相同,前面已经说过,在任务运行过程中,我们无法控制,这个时间可能是几百毫秒,也有可能是几分钟。

此外,用户也不是每次购买都有优惠券奖励,推送任务到队列然后每次检查是否有优惠券奖励资格存在资源浪费:占据了队列中的位置,但是不做任何事情。

更早决策

要解决上述问题,需要在订单创建时就判断是否有领取优惠券的资格,而不是在发送优惠券的任务中,如果没有领取优惠券资格干脆不将其推送到队列。要实现这个功能,可以在监听器类中定义 shouldQueue 方法来实现:

在推送事件监听器到队列之前,会先通过 shouldQueue 方法判断用户是否有领取优惠券的资格,如果有的话才将其推送到队列,否则不会推送。

这样一来,我们就可以做到在订单创建时立即判断用户是否有领取优惠券的资格,以避免浪费不必要的系统资源。


点赞 取消点赞 收藏 取消收藏

<< 上一篇: 为用户聚合来自不同社交平台的消息流

>> 下一篇: 异步发送应用部署通知