在后台开发中,很多情况需要用到队列来处理业务逻辑,前几天亲自实践了一下Laravel的队列功能,在此分享一蛤。
使用情景
举几个使用到消息队列的栗子
- 在处理商城的库存问题,简单的实现是当用户的某件商品下单了,库存就相对应减少,但是当并发量一大时,比如A,B两个顾客同时下单,如果不使用队列可能会造成库存错误,使用队列了保证不同时执行两个减库存操作(一个阻塞掉了另外一个)。
- 当一个服务器要处理一个请求端发来的多组大量数据,而且每组数据处理起来的时间会很慢时,可以采取异步队列,就相当于请求端发过来的数据先存着,放进队列里,然后再异步处理数据(即处理数据的过程不在请求的生命周期里)
- 由于比如用户登陆了某网站之后,后台就过xx分钟给用户发延迟邮件来推送广告(怎么有点流氓(°Д°)),这里也可以用到异步队列,虽然可以用设计定时任务来轮询检查的笨方法,不过感觉用轮询来实现异步不是真正的异步
扯了这么多,刚回到正题了—实践
实验环境
- Laravel 5.2
- 系统: Windows(wamp) or Linux(lnmp)
步骤
配置
laravel提供了多种消息队列驱动: sync(同步) database beanstalkd sqs redis
默认是sync,不过实际上其他几种异步队列多点,本章先用database练练手,前几天刚服务器刚配好redis,迟点再补上redis的吧~
so,先到 /.env (默认根目录为你的项目目录) 中 填上 QUEUE_DRIVER=database ,当然你也可以到/config/queue.php的default中将sync修改,但是建议修改.env
建表
修改完队列驱动后,就用Laravel的migration自动生成队列需要的表,到根目录执行artisan
|
|
此时,数据库中就会出现jobs表和failed_jobs表
创建任务类并编写任务类
执行artisan命令快速生成任务类并在/app/jobs/ 目录下面找到刚创建的任务
|
|
刚自动生成的任务类包含了两个初始方法
|
|
构造方法__construct用于你给任务类传值or对象用,至于handle方法则是队列任务的处理(即给排在队列里的任务如何执行业务逻辑)
这时就有个问题了,排在队列里的任务如果处理时失败了,或者因不符合某些业务逻辑需要进行统一的失败操作,那该怎么办呢?
此时可以在这个任务类中添加一个failed方法
|
|
这样laravel就可以识别得到,当一个任务在执行handle方法时,遇到异常了(Throw Exception),laravel就会自动捕获异常并执行failed方法,并在failed_job表中记录信息。这个用来管控你看不见摸不着的异步队列任务比较有用,你可以根据业务逻辑在failed方法里将错误信息输出到自己的日志或者其他操作。
任务类里自带的几个有用方法
通过artisan生成的任务类里,由于使用(use)了InteractsWithQueue这个trait,所以可以使用到里面有几个自带的方法
attempts
作用:返回任务的已经执行次数(eg.可以用于在handle方法中判断任务执行超过n次时就抛出异常来执行failed方法等等)
食用方法:在任务类的handle方法或者failed方法中,无参
|
|
delete
作用:在队列中删除该任务(jobs表里删除)
食用方法:同上
|
|
release
作用:将当前任务再次放进队列中,可传入参数,来控制同一个任务两次运行之间的等待时间
食用方法:同上,不过有参,参数为int类型 表示多少秒
|
|
目前任务类里比较常用的就是这几个方法了。
执行任务
代码里要做的事
编写好任务类之后,你可以在你的控制器里面创建任务,并且将它调度执行(dispatch)
|
|
如果想设置延时
|
|
如果想指定队列(分类)
|
|
shell里要做的事
当代码里做的事做好了之后,我们一个请求过去服务器的项目程序就能启动任务了吗?不能,因为之前我们说过,除了sync其他驱动都是异步的,而php是单线程的(意味着一个开启一个php脚本相当于开启一个进程,而这个进程里面只有一个线程),所以我们一个请求只有一个线程,因此我们需要开多一个进程来进行队列监听了,当监听脚本监听到队列有动静时,就对队列进行处理。
|
|
测试
- 开始监听
- 发送请求(可以用postman试试)到指定控制器来调度队列任务
- 观察正在执行监听任务的shell(ctrl+c 退出监听)
小结
通过实践,算是掌握了laravel的队列使用,下面就提一下我遇到的坑点以及改进吧~
注意事(坑)项(点)
当你使用了onQueue方法来给任务分类然后调度时,监听的命令该怎么写?
一开始我天真的认为一句 php artisan queue:listen 就能监听所有分类的任务,结果之后发现我定义的一个分类的任务一直不执行,看了文档又找不到缘故
|
|
|
|
翻了一下帮助命令
|
|
可以看到
所以正确的执行姿势应该是
|
|
还有其他queue的命令就自己发掘或者看文档吧~
优化
- 为了保护监听进程(失败后重启等),我们可以尝试用一下Supervisor(linux)
- 因为现在很多缓存都用redis,所以这个队列任务如果用redis的话,就可以一举两得了~~
以上两点列入到Todo list中,有空再捣鼓一蛤!!
感谢
感谢 Laravel学院 的中文文档 !