什么是后台任务?
后台任务就是运行在程序流程以外的任务,毕竟 PHP 作为网站后台语言是需要在执行后立即返回数据的,而且一般服务器都有设置执行的超时时间,所以使用 PHP 去完成一些比较耗时的后台操作就有些问题了。
通常在做一些比较耗时的操作时都会想到使用一个后台任务以及任务队列,在流程外执行这些操作,并立即返回给前台一个正在执行的提示。
我们来看一个实例:一个社交网站,某用户修改了他的的个人资料中的所在地。
一般的流程是这样的:
PHP 并不是多线程的,所以所有任务必须在前一个任务完成后再能开始,这就会致使用户等待较长的时间。上面例子中,大约3.7秒后用户才能获取到来自服务器的反馈,用户体验较差。
需要注意的是在整个流程中最重要的仅仅是第一个步骤:Update Database(更新数据库)。如果这个过程失败了,其它的流程都不会被执行;如果成功就可以发送一条执行成功的消息给用户,如:“您的所在地信息已经更新成功”,因为最终的结果是要反馈信息给用户,在失败时同样会返回一条“未能成功更新所在地”的信息。
其它流程(如刷新缓存、发送邮件等)对于返回给用户的页面来说并不是必须的,那么为什么要让用户等待这些时间去得到非必需的结果?如果仅仅响应用户请求的操作并且立即返回结果,让那些不重要的流程稍后再去进行岂不是更好?
理想的工作流程
这样会大大提升用户体验,因为用户仅需 0.3 秒的等待便可以得到响应。那些不重要的流程全部交给主流程之外的后台任务去执行。
一个简单的类比
如果你仍不明白究竟发生了什么:
- 用户 Lambda 告诉 Mr.Server(服务器)更新他的所在地信息
- Mr.PHP 是为 Mr.Server 工作的,现在他接收了这个任务
- Mr.PHP 为 Lambda 更新了数据库
- Mr.PHP 在公司便签上留言:“我已经更新了 Lambda 的所在地信息,请更新缓存、发送通知邮件,并为他推荐些好友”
- Mr.PHP 告诉用户 Lambda:“已经给您更新好了!”
- Mr.PHP 现在空闲下来了,准备接收下一个任务
但是 Mr.PHP 在便签上留言之后发生了什么?
- Mr.PHP#2,另一个给 Mr.Server 打工的小伙子取下了这张便签
- 他回到自己的办公桌上并按照便签上的内容开始工作
- 当他完成工作后休息一会儿并去取走下一个便签,周而复始
在上面的例子中,Mr.PHP#2 就是worker,一张张便签就是queue(队列)。便签纸上的任务就是jobs。
Mr.PHP 以及和他做同样工作的人都可以往墙上贴便签,即往队列中添加任务。Worker 的主要工作就是每隔一段时间去查看墙上有没有便签,并取下便签去执行上面所写的工作。
这些任务(jobs)就是后台任务,“前台任务”是指被直接处理的流程(即Mr.PHP的工作)。而后者并不知道(或者说不需要知道)这些任务是由如何、由谁以及何时被执行的。
一旦添加了 Jobs,只有 Worker 可以操作这 Jobs,而“前台任务”只知道这些 Jobs 等会儿会执行。
Worker 和 Queue 不一定是一个。多个 Worker 也可以执行同一个 Queue,一个 Worker 也可以执行多个 Queue。Worker 在队列中轮循的间隔时间也不固定,可以是 5 秒,也可以是 15 秒、1 分钟,等。
一个Queue System(队列系统) 被用来管理所有的 Jobs,包括保存、排序(优先级)等。
后台任务的重要性
后台任务在生活中随处可见。例如你打电话购买一台虚拟主机:
- 接线员在电话里向你询问信用卡信息
- 他向银行核对信息,银行批准交易
- 他为你创建账户,并给你一个登录口令
- 他让你等 15 分钟,15 分钟后主机将可用
优点
如果没有后台任务,你可能需要等很长时间。因为银行需要付款给主机供应商,在这里使用后台任务只需要告诉银行付款给主机供应商,接线员只需要确认你的信用卡是有效的,同时主机供应商确信银行会付款给他们,并为你创建账户。
同样,你可能需要继续等待 15 分钟,因为接线员那边需要让技术人员为你开通主机并等待开通成功。这样不仅浪费了你的时间,也浪费了接线员的时间。
从编程的角度想,后台任务由另一个进程启动,这就意味着无论后台任务发生了什么都不会影响到主流程,无论是异常还是错误。
缺点
后台任务虽然节省了大量时间,但你却失去了对这些任务的控制。一旦你把任务发送到队列,也许只有上帝知道它们怎么样了~你还有 Worker 在工作吗?它们有没有被释放?它们在执行正确的 Queue 吗?(不知道)
确信你发送到队列的 Jobs 有被 Worker 正确的执行将会是你的责任。经常会出现的错误如:
- 发送 Jobs 到一个名为“achievement”的Queue,而Worker执行的是名为“achivement”的 Queue
- 重新启动了服务,却忘了重新启动 Worker
- 没有监视你的 Worker 工作状态,致使一个致命错误发生并终止了 Worker 的运行
以前面虚拟主机的例子,接线员告诉你主机将会在 15 分钟内上线。他怎么会知道?因为手册上是这么写的,他也不能保证主机就一定会在 15 分钟内上线。
Worker 执行自己的工作(技术人员为主机安装软件)不与外面的环境沟通,如果他现在有大量的工作,你的应该会在工作(Queue)的最下面。
为了获取 Worker 的工作状态,你不得不让 Worker 发送大量的报告,如何时开始工作、发送邮件/通知给管理员等,Worker 一旦开始你将不能再命令它们做什么,但它们可以告诉你它们在做什么。
但仍然有些错误是不能获取到的,如致命错误(出现致命错误时程序会终止,也就不会发送任何信息出去了)。唯一能发现它们的方法就是监视服务器的PHP错误日志。这个相当重要,一旦没有任何Worker在处理工作,这将是一件麻烦事。
本文由冰翼翻译自Kamisama.me
Comments
注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。