Laravel 自己实现了队列的机制,如果要看源码解析,这两篇文章写的很好。
- https://laravel-china.org/articles/4169/analysis-of-laravel-message-queue
- https://laravel-china.org/articles/7037/laravel-queue-analysis-of-message-queue-tasks-and-distribution-source-code
注:第二篇文章因为七牛存储不再提供域名服务的原因,导致图片无法访问,我把珍藏的截图贡献出来。
本篇要讲的是在应用中遇到的坑。
Laravel 常用的队列驱动
- Redis
- beanstalk
- sqs
- database
在MySQL事务中
队列一般用于
比较耗时或不太重要
的任务处理,如果是不太重要的任务(比如:发邮件、发消息),那就没必要在事务中进行写队列的操作,即使写入队列不成功也没什么大不了的。
但是如果是比较重要但耗时的任务,为了能快速响应用户,需要用到队列。因为任务是比较重要,所以需要保证数据入库
和写入队列
都成功。这种情况下就有一个很重要的点,业务代码需要先写数据库后再写入队列,因为MySQL事务回滚并不会回滚队列
队列的异常、错误
无论选择哪一种队列驱动,Laravel 都会把错误的任务写入failed_jobs表中(当然,如果你设置了重试次数,那就是重试之后仍然失败后写入)。
- 当代码异常情况下,如果捕捉就不会写入failed_jobs表中。
- 当代码错误情况下,就一定会写入(除非mysql写入失败)。
单个任务执行时间
'redis' => [
'driver' => 'redis',
'connection' => env('QUEUE_REDIS_CONNECTION', 'default'),
'queue' => 'default',
'retry_after' => 60
]
在 queue.php
文件配置中(以redis 为例),retry_after
代表如果单个任务在60秒内没有ack,就会把任务重新放入队列。简单的讲:如果单个任务执行时间超过60秒,就会重复执行。
数据库驱动
如果用 DB 作为驱动,队列定时回去数据库查询需要执行的任务,
select * from `nw_jobs` where `queue` = default and ((`reserved_at` is null and `available_at` <= 1548066079) or (`reserved_at` <= 1548062479)) order by `id` asc limit 1 for update
这条 SQL 会用到主键索引,所以只会锁单条记录。
其他
- 我们为什么要使用队列,有两个比较重要的原因:异步和重试
- 由队列就可以联想到消息服务,消息服务就需要满足以下功能
- 消费者回调
- 重发机制
- 消息幂等性