9

考虑一个 PHP Web 应用程序,其目的是接受用户请求以启动通用异步作业,然后创建一个工作进程/线程来运行该作业。这些作业不是特别占用 CPU 或内存,但预计会经常阻塞 I/O 调用。每秒不应启动超过一到两个作业,但由于运行时间长,可能同时运行许多作业。

因此,作业并行运行至关重要。此外,每个作业都必须由一个负责杀死挂起的工作人员、根据用户请求中止工作人员等的管理器守护进程进行监控。

实施这样的系统的最佳方法是什么?我可以看到:

  1. 从经理那里分叉一个工人——这似乎是最低级别的选择,我必须自己实施一个监控系统。Apache 是 Web 服务器,因此这个选项似乎需要通过 FastCGI 启动任何 PHP 工作者。
  2. 使用某种作业/消息队列。(gearman、beantalkd、RabbitMQ 等) - 最初,这似乎是显而易见的选择。经过一些研究,我对所有选项都有些困惑。例如,Gearman 看起来像是为大型分布式系统设计的,其中有固定的工人池......所以我不知道它是否适合我的需要(每个工作一个工人)。
4

3 回答 3

8

好吧,如果你在 Linux 上,你可以用pcntl_forkfork children off。“主人”然后看着孩子们。每个孩子都完成了自己的任务,然后正常存在。

就个人而言,在我的实现中,我从来不需要消息队列。我只是在“master”中使用了一个带锁的数组。当一个孩子找到工作时,它会写一个带有工作ID号的锁文件。然后主人会等到那个孩子退出。如果孩子退出后锁定文件仍然存在,那么我知道任务没有完成,并重新启动具有相同作业的孩子(删除锁定文件后)。根据您的情况,您可以在一个简单的数据库表中实现队列。在表中插入作业,并每隔 30 或 60 秒检查一次主表中的新作业。然后只有在孩子完成后才从表中删除它们(并且孩子删除了锁定文件)。如果您一次运行多个“主”,这会出现问题,但您可以实现一个全局“

而且我不建议使用 FastCGI 进行分叉。它可能会导致一些非常模糊的问题,因为环境是要持续存在的。相反,如果您必须拥有 Web 界面,请使用 CGI,但最好使用 CLI 应用程序(一个守护程序)。要与其他进程的主进程交互,您可以使用套接字进行 TCP 通信,或者创建一个FIFO 文件进行通信。

至于检测挂起的工作人员,您可以实现一个“心跳”系统,其中子SIG_USR1进程每隔这么多秒向主进程发出一个。那么如果你在那个时间两三次都没有收到孩子的消息,它可能会被挂起。但问题是由于 PHP 不是多线程的,你无法判断一个孩子是否挂起,或者它是否只是在等待阻塞资源(如数据库调用)......至于实现“心跳” ,您可以使用刻度函数来自动执行心跳(但请记住,阻塞调用仍然不会执行)...

于 2010-08-18T14:36:04.310 回答
1

workerpool 可能很有趣:

https://github.com/qxsch/WorkerPool

https://github.com/qxsch/WorkerPool/blob/master/examples/asyncExample.php

于 2014-05-06T16:18:06.643 回答
1

当您使用 pcntl_fork 运行具有许多作业的异步一项任务时,或者您将每(s)秒创建持久性查询,小心高 CPU 消耗,您可能会因为无法再次分配内存而挂起处理内存,我认为最好的选择您可以使用 Gearman 完全构建,也可以尝试使用 IronWorker 等云工作者。

于 2016-03-31T04:59:28.903 回答