49

我目前正在尝试在 php 中实现一个作业队列。然后队列将作为批处理作业处理,并且应该能够并行处理一些作业。

我已经做了一些研究并找到了几种实现它的方法,但我并没有真正意识到它们的优缺点。

例如,通过多次调用脚本来执行并行处理,fsockopen如下所述:
Easy parallel processing in PHP

我发现的另一种方法是使用这些curl_multi函数。
curl_multi_exec PHP 文档

但我认为这两种方法会增加在主要在后台运行的队列上创建批处理的开销?

我还阅读了pcntl_fork这似乎也是一种处理问题的方法。但是,如果您真的不知道自己在做什么(就像我现在一样),那看起来会变得非常混乱。

我也看过Gearman,但在那里我还需要根据需要动态生成工作线程,而不仅仅是运行一些,然后让 gearman 作业服务器将其发送给空闲工作线程。特别是因为在执行一项作业后线程应该干净地退出,以免最终发生内存泄漏(该问题中的代码可能并不完美)。
Gearman 入门

所以我的问题是,你如何处理 PHP 中的并行处理?你为什么选择你的方法,不同的方法可能有哪些优点/缺点?

4

9 回答 9

22

我用exec()。它简单而干净。您基本上需要构建一个线程管理器和线程脚本,这将满足您的需求。

我不喜欢fsockopen(),因为它会打开一个服务器连接,这会建立并可能达到 apache 的连接限制

curl出于同样的原因,我不喜欢函数

我不喜欢pnctl,因为它需要可用的 pnctl 扩展,并且您必须跟踪父/子关系。

没玩过gearman...

于 2011-05-24T08:19:46.410 回答
14

好吧,我想我们有 3 个选项:

A. 多线程:

PHP 本身不支持多线程。但是有一个名为 pthreads ( https://github.com/krakjoe/pthreads ) 的 PHP 扩展(实验性)可以让您做到这一点。

B. 多进程:

这可以通过 3 种方式完成:

  • 分叉
  • 执行命令
  • 管道

C. 分布式并行处理:

这个怎么运作:

  1. 应用程序将“可以是 JSON 格式”的Client数据(AKA 消息)发送到“可以是本地或外部网络服务”的引擎(MQ 引擎)</li>
  2. MQ Engine数据“主要在内存中,也可以在数据库中”存储在队列中(您可以定义队列名称)
  3. Client应用程序要求 MQ 引擎按顺序(FIFO 或基于优先级)处理数据(消息)“您也可以从特定队列请求数据”。


一些 MQ 引擎:

  • ZeroMQ(不错的选择,很难使用)一个面向消息的 IPC 库,是 Erlang 中的消息队列服务器,将作业存储在内存中。它是一个充当并发框架的套接字库。在集群产品和超级计算方面比 TCP 更快。
  • RabbitMQ(不错的选择,易于使用)自托管,企业消息队列,不是真正的工作队列 - 而是可以用作工作队列但需要额外语义的消息队列。
  • Beanstalkd(最佳选择,易于使用)(Laravel 内置支持,由 facebook 构建,用于工作队列) - 有一个非常好的“Beanstalkd 控制台”工具
  • Gearman (问题:分布式处理的集中代理系统)
  • Apache ActiveMQ 是 Java 中最流行的开源消息代理,(问题:很多错误和问题)
  • Amazon SQS (Laravel 内置支持,托管 - 所以不需要管理。不是真正的工作队列,因此需要额外的工作来处理语义,例如埋葬工作)
  • IronMQ (Laravel 内置支持,用 Go 编写,提供云版本和本地版本)
  • Redis (Laravel 内置支持,没有为它设计的那么快)
  • Sparrow (用基于 memcache 的 Ruby 编写)
  • Starling (用 Ruby 编写,基于 memcache,内置于 twitter)
  • Kestrel (只是另一个 QM)
  • 卡夫卡 (在 Scala 的 LinkedIn 上编写)
  • EagleMQ 开源、高性能、轻量级的队列管理器(C 编写)

更多可以在这里找到:http: //queues.io

于 2016-04-06T02:53:32.120 回答
4

如果您的应用程序要在 unix/linux 环境下运行,我建议您使用分叉选项。它基本上是儿童游戏来让它发挥作用。如果分叉不是一个选项,我已经将它用于 Cron 管理器并有代码将其恢复为 Windows 友好的代码路径。

正如您所说,多次运行整个脚本的选项确实增加了相当多的开销。如果您的脚本很小,它可能不是问题。但是您可能会习惯按照您选择的方式在 PHP 中进行并行处理。下次当你的工作使用 200mb 的数据时,这很可能是个问题。因此,您最好学习一种可以坚持的方法。

我还测试了 Gearman,我非常喜欢它。有几件事情需要考虑,但总的来说,它提供了一种非常好的方法,可以将作品分发到运行以不同语言编写的不同应用程序的不同服务器。除了设置它之外,在 PHP 或任何其他语言中实际使用它是......再一次......孩子们玩。

对于您需要做的事情,这很可能是矫枉过正。但在处理数据和作业时,它会让您看到新的可能性,因此我建议您单独尝试使用 Gearman。

于 2011-05-24T10:20:07.440 回答
4

下面是 PHP 中并行处理的几个选项的总结。

安培

Checkout Amp - 异步并发变得简单- 这看起来是我见过的用于并行处理的最成熟的 PHP 库。

Peec 的过程类

这个类发布在PHP 的 exec() 函数的评论中,并为分叉新进程和跟踪它们提供了一个真正简单的起点。

例子:

// You may use status(), start(), and stop(). notice that start() method gets called automatically one time.
$process = new Process('ls -al');

// or if you got the pid, however here only the status() metod will work.
$process = new Process();
$process.setPid(my_pid);

// Then you can start/stop/check status of the job.
$process.stop();
$process.start();
if ($process.status()) {
    echo "The process is currently running";
} else {
    echo "The process is not running.";
}

其他选项比较

还有一篇很棒的文章Async processing or multitasking in PHP解释了各种方法的优缺点:

门卫

然后,还有这个简单的教程,它被包装在一个名为Doorman的小库中。

希望这些链接为更多研究提供有用的起点。

于 2018-05-08T00:54:45.430 回答
3

首先,这个答案是基于linux OS env的。还有一个pecl扩展是并行的,你可以通过发布安装它pecl install parallel,但它有一些先决条件:</p>

  1. 安装 ZTS(Zend Thread safety) Build PHP 7.2+ 版本
  2. 如果你通过源代码构建这个扩展,你应该检查 php.ini 之类的配置文件,然后添加extension=parallel.so到它

然后查看完整的示例要点:https ://gist.github.com/krakjoe/0ee02b887288720d9b785c9f947f3a0a 或 php 官方网站网址:https ://www.php.net/manual/en/book.parallel.php

于 2019-11-06T04:08:47.917 回答
2

我更喜欢 exec() 和 gearman。exec() 很简单,没有连接,内存消耗更少。gearman 应该需要一个套接字连接,并且 worker 应该需要一些内存。但是 gearman 比 exec() 更灵活、更快。最重要的是它可以将worker部署在其他服务器上。如果工作是时间和资源消耗。我在我当前的项目中使用 gearman。

于 2011-05-24T10:09:22.987 回答
2

我使用 PHP 的 pnctl - 只要您知道自己在做什么,它就很好。我了解您的情况,但我认为理解我们的代码并不难,在实现 JOB 队列或并行进程时,我们只需要比以往任何时候都更加自觉。

我觉得只要你完美地编码并确保流程在偏离路线时完美,你应该在实施时牢记并行过程。

你可能犯错误的地方:

  1. 循环 - 应该能够由 GLOBAL 变量处理。
  2. 处理一些事务集——同样只要你定义正确的集合,你应该能够完成它。

看看这个例子 - https://github.com/rakesh-sankar/Tools/blob/master/PHP/fork-parallel-process.php

希望能帮助到你。

于 2011-05-24T10:30:48.843 回答
2

使用原生 PHP (7.2+) Parallel,即:

use \parallel\Runtime;

$sampleFunc = function($num, $param2, $param3) { 
    echo "[Start: $num]";  
    sleep(rand(1,3) ); 
    echo "[end:$num]";   
};

for($i = 0; $i < 11; $i++) { 
    \parallel\run($sampleFunc,  [$param1=$i, $param2=null, $param3="blabla"] );
}
for ($i = 0; $i < 11; $i++) {
    echo " <REGULAR_CODE> ";
    sleep(1);
}

(顺便说一句,您需要通过硬路径安装支持 ZTS 的 PHP,然后启用并行。我建议 phpbrew 这样做。)

于 2021-04-24T10:45:28.310 回答
1

'Easy parallel processing in PHP'中描述的方法非常可怕-原理还可以-但是实现???正如您已经指出的那样, curl_multi_ fns 提供了一种更好的方法来实现这种方法。

但我认为这两种方式会增加很多开销

是的,您可能不需要客户端和服务器 HTTP 堆栈来完成这项工作 - 但除非您为 Google 工作,否则您的开发时间比硬件成本要昂贵得多 - 并且有很多工具可以管理 HTTP /分析性能 - 并且有一个定义的标准涵盖状态通知和身份验证等内容。

您实施解决方案的很多方式取决于您需要的事务完整性级别以及您是否需要按顺序处理。

在您提到的方法中,我建议关注使用 curl_multi_ 的 HTTP 请求方法。但是,如果您需要良好的事务控制/订单交付,那么您绝对应该在消息源和处理代理之间运行代理守护程序(这里有一个编写良好的单线程服务器,适合用作代理的框架。请注意,处理代理应一次处理一条消息。

如果您需要一个高度可扩展的解决方案,那么请查看适当的消息队列系统,例如RabbitMQ

高温高压

C。

于 2011-05-24T10:39:47.307 回答