业务背景:定时给各位单位下的员工发送微信模板消息
问题描述:多个员工经常反应收到重复的模板消息
主要技术栈:laravel6+redis+supervisor
解析历程:
一开始反应说是收到2条一样的消息,各种查代码,但始终无法在代码层面上发现有何错误。就先把锅甩给微信了,说是微信那边的问题。后来又反应存在重复3条的情况。诶,这就不太正常了呀!于是开始认真查bug啦(其实一开始就很认真了~)。
大概的业务流程就是查出当前要发送的单位,将该单位集合作为参数传给redis队列,在队列中查找各个单位下的员工并发送消息。
查bug嘛,首先肯定是从日志入手咯!打开supervisor的日志定位到其中一个收到三条消息的用户,确实存在发送了3次的记录。但是该队列却仍然失败了(在failed_jobs)。在经历了一番查找后并结合三次发送记录的时间间隔(大概90秒),确定是由于队列执行超时(没有设置timeout超时时间),经过retry_after参数默认设置的时间后,任务在执行了 90 秒后将会被重新放回队列而不是删除。重新放回去的次数就是retries设置的参数。重试该次数后,即认为该任务失败,将该任务放进failed_table表中。
解决:supervisor配置文件增加timeout参数 config/queue.php redis retry_after参数。
retry_after
配置项和 --timeout
命令行配置并不同,但将它们同时使用可以确保任务不会丢失并且任务只会成功执行一次。
注意:
--timeout
的值应该比你在retry_after
中配置的值至少短几秒。这会确保处理器永远会在一个任务被重试之前中止。如果你的--timeout
值比retry_after
的值长的话,你的任务可能会被执行两次。
failed_table exception如下:
Illuminate\Queue\MaxAttemptsExceededException: App\Jobs\SendMessage has been attempted too many times or run too long. The job may have previously timed out. in /home/php_web/business_admin/vendor/laravel/framework/src/Illuminate/Queue/Worker.php:632
解惑文档如下:
https://learnku.com/docs/laravel/6.x/queues/5168#max-job-attempts-and-timeout