本书作者对于基于 PSR-7 的现代 web 开发和 Laravel 框架的理解是十分深刻的。本书价值所在不是讲实践也不是讲 demo 来水一本书,也不是 cookbook ,而是结合源码进行的对于 Laravel 运行机制的分析,朴实但没一句废话的讲解十分难得,研究员风格的叙述凸显作者功力深厚。
在本书中的大部分时间里,以 Laravel In Depth 的口吻讨论的 app 生命周期都是一个 HTTP 请求。如果把每次 php-cgi 读取项目文件并载入内存并执行的 app 看作是一个 laravel app 的副本,那么,本书的大部分时间里讨论的是一个请求带动的 laravel app 副本(生命周期就是一个 HTTP 请求)。
但是 到 laravel 队列这里就不是这样了,在此我把我的理解写出来自娱自乐一下。因为从直觉上,queue work 队列执行任务本身就是耗时的。这里似乎需要一个需要常驻后台的系统,而且需要监听新任务的到来(它并不是 在 HTTP 请求的背景之下 计算出 HTTP 的响应的首部和主体、发送 HTTP 响应,结束这一次伺服:若不考虑慢网速 应该是毫秒级,而 —— 一个需要常驻后台的系统 —— 队列里的任务 甚至可以故意被设置得让它 delay 几分钟 仿佛是交由一个有计时器的 daemon 在接管的)。
为什么有一部分 laravel app 要常驻后台以便处理耗时任务并达到异步任务处理的效果? —— guess what? 这就是它被我成为 “队列系统” 的原因。
你完全可以把 “队列系统” 和 “请求处理系统” 看作是互不相干的2个系统(一个有监听功能 有自己的数据源 能管理任务并接受任务执行情况的报告、一个没有监听功能 有自己的数据源 能发出 HTTP 响应 能很快结束一次伺服)。2个系统的交点在于 请求处理系统 可以给 队列系统 输送一个新任务(可以很简单如 “将作业推送到队列:把 请求处理系统 来的提请/任务/作业 委托给 队列系统 去做” https://learnku.com/laravel/t/40745 注意观察2个系统之间有哪些直觉上的交点和独立点 比如 第二个系统是不是要自己独立启动阿、第二个系统是不是要在自己完成一个任务之后给第一个系统一些返回阿(回调)、第一个系统是不是不应该也不能因为第二个系统的卡顿而受影响阿否则分2个系统还有什么意义、第二个系统是不是一个独立存在的系统 像是一个长期存在的进程 即 一个守护进程 ( 常驻后台 也称 daemon 如 mysqld ) 阿,可以很复杂如 https://learnku.com/articles/42432 处理了队列任务完成之后的回调什么的,观察2个系统是如何交互的,队列系统在任务完成之后是如何激活前者的)
对于 “监听” 这回事,值得参考的 2 篇文章,它们值得参考的原因是提到了 supervisor 、提到了
注意一旦 php artisan queue:work
命令开始执行,它会一直运行直到它被手动停止或终端被关闭。也就是说,它是一个常驻后台的进程(daemon)。就像面对任何一个 daemon 需要考虑的问题一样,laravel queue work daemon 需要考虑如果意外中断需要自动重启的问题,所以需要 supervisor 来负责。
https://learnku.com/docs/laravel/5.8/queues
https://learnku.com/laravel/t/40745
Supervisor 是用 Python 开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。
如果把每次 php-cgi 读取项目文件并载入内存并执行的 app 看作是一个 laravel app 的副本,那么 这是一个带队列系统的 laravel app 副本。它不同于直接由一个请求带动的 laravel app 副本(生命周期就是一个 HTTP 请求),它会更长命,会常驻后台(直到任务队列清空为止),并且会继续监听新任务的到来。它 和 处理 HTTP 请求的副本是各自独立的。(这就是它,一个带队列系统的 laravel app 副本,必须有自己的生命周期的原因)
一个带队列系统的 laravel app 副本,php-cgi 生命周期由 supervisor 托管,并且负责挂了之后的自动重启。
一个不带队列系统的 laravel app 副本,php-cgi 生命周期由 php-cgi 的调用者托管(比如 php-fpm)
--- 额外的说明1 - 关于进程的自动重启 ---
让一个进程在后台运行,或者说让一个命令在后台运行,最简单的办法就是在命令后面之后加一个 & 。
对于常驻后台的进程,若意外中断了就中断了,挂了都没人知道。所以你会希望控制一个常驻后台的进程在意外挂了的时候自动重启。
pm2 、 supervisor 等进程管理程序的作用:让一个命令保持在后台运行、若意外挂了可让它自动重启。比如 pm2 的常用用法是 注册一个命令(比如 node app.js 是一个命令,它正常情况下可以监听 80 端口作为服务器,如果正常运行就正常,如果挂了就 404 )然后接管它的生命周期,当它意外退出或意外挂了的时候,pm2 可以检测到它挂了,可以自动重启它。参考 https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-16-04 什么时候需要进程开机启动的另一个例子是 如果你想开启激动一个脚本(比如 app.js ),那么启动之后 等于你启动了一个 node 进程,那么搜索 linux daemon process management (开机启动)或 ensure a process is always running (自动重启) 就会找到 supervisor 。
------------------------------------------------------------
关于 “系统” 的意识:为什么说是有2个系统呢?明明不是一个 laravel app 吗?说2个系统,是因为它们各有生命周期,这就足以让人意识到 这是两个系统了(各有生命周期的管理,一个由 php-fpm 管理,一个由 supervisor 管理)。也就是说,在系统意识之下,只要是2个生命周期的玩意就是2个系统,这可以做到一个系统对另一个系统的不干扰(当然这里涉及到热部署的问题,如果2个系统用的是同一套代码,让 supervisor restart xx_program_name 即 重启第二个系统 )。
队列使用的例子
https://www.jianshu.com/p/3371666b7880
https://www.jianshu.com/p/78cfea85cadc 回调处理,处理办法一部分 写在了 AppServiceProvider 类的 boot() 函数里,另一部分写在了 SendMailJob 类的 handle() 函数里。
非常清晰:AppServiceProvider 类的 boot() 函数,属于队列管理:整个队列清空之后(所有任务完成之后) 发个报告;
SendMailJob 类的 handle() 函数里,属于任务管理:这个的队列里的一个任务完成之后 发个报告。参考 Laravel 文档 只不过那里没给例子。
关于事情的可计划性、两个系统的交点的可计划性,参考
https://zhuanlan.zhihu.com/p/190243220 军情六处是怎样从克格勃手里救出一个人的
--- 额外的说明2 - 关于队列系统 ---
如果用一句话说明 “队列系统” 是什么:由 php artisan queue:work --max-jobs=1000
命令启动、常驻后台、由 supervisor 接管生命周期,和 laravel app 共用一套代码的一个守护进程 ( daemon )。
(既然是 2个系统,生命周期都被分管了,那么同步和异步这个概念就没有意义,自然都好像是异步的)
---------------------------------------------
> 我来回应