PHP 中的并行处理 - 你是怎么做的?

Posted

技术标签:

【中文标题】PHP 中的并行处理 - 你是怎么做的?【英文标题】:Parallel processing in PHP - How do you do it? 【发布时间】:2011-08-31 17:30:37 【问题描述】:

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

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

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

我发现的另一种方法是使用 curl_multi 函数。curl_multi_exec PHP docs

但我认为这两种方式会增加相当多的开销,以便在应该主要在后台运行的队列上创建批处理?

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

我还查看了Gearman,但我还需要根据需要动态生成工作线程,而不是只运行几个,然后让 Gearman 作业服务器将其发送给空闲的工作线程。尤其是因为线程应该在执行完一项作业后干净地退出,以免最终发生内存泄漏(该问题中的代码可能并不完美)。Gearman Getting Started

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

【问题讨论】:

接受了 Quamis 的回答,因为这就是我现在要做的事情。其他答案也很好,将来也可能会研究他们的方法。再次感谢。 有关不同方法的详细讨论,请参阅***.com/questions/209774/does-php-have-threading/… 【参考方案1】:

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

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

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

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

从来没有玩过齿轮人...

【讨论】:

但是您的线程管理器会是什么样子?我遇到的主要问题是什么是创建这些线程并让它们在完成工作后干净地退出的好方法。 exec 阻塞直到命令完成,所以我不能并行运行作业还是我错了? 你可以在后台运行东西:) ***.com/questions/45953/… 啊,当然...谢谢,没有真正考虑到这一点:) 实际上在 exec php 手册的 cmets 中甚至已经有一个 example code,这也很容易显示如何跟踪然后是进程。 必须记住,出于安全原因,某些服务器不允许 exec() @Gacek:这实际上是特定于 PHP 的配置,而不是特定于服务器的。如果您没有访问服务器配置的权限,无论如何都很难进行并行处理【参考方案2】:

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

【讨论】:

【参考方案3】:

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

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

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

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

【讨论】:

【参考方案4】:

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

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

你可能会犯错误的地方:

    循环 - 应该能够由 GLOBAL 变量处理。 处理某些事务集 - 只要您正确定义了这些事务集,您应该能够完成它。

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

希望对你有帮助。

【讨论】:

【参考方案5】:

“PHP 中的简单并行处理”中描述的方法非常可怕——原理还可以——但是实现???正如您已经指出的那样,curl_multi_fns 提供了一种更好的方法来实现这种方法。

但我认为这两种方式会增加相当多的开销

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

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

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

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

HTH

C.

【讨论】:

【参考方案6】:

我想我们有 3 个选项:

A.多线程:

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

B.多进程:

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

分叉 执行命令 管道

C.分布式并行处理:

工作原理:

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

一些 MQ 引擎:

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

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

【讨论】:

pthreads 项目停止并且未在 php 7.4.x 上编译 github.com/krakjoe/pthreads/issues/929 php 8 现在支持多线程。php.net/parallel 不仅仅是php 8,php 7.2+也支持parallel【参考方案7】:

这里总结了 PHP 中并行处理的几个选项。

AMP

Checkout Amp - Asynchronous concurrency made simple - 这看起来是我见过的用于并行处理的最成熟的 PHP 库。

Peec 的进程类

本课程发布于the comments of PHP's exec() function,为派生新进程和跟踪它们提供了一个真正简单的起点。

例子:

// 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 解释了各种方法的优缺点:

pthreads extension(另见this SitePoint article) Amp\Thread Library hack 的异步(需要运行 Facebook 的 HHVM) pcntl_fork popen fopen/curl/fsockopen

门卫

然后,还有this simple tutorial,它被包装到一个名为Doorman 的小库中。

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

【讨论】:

【参考方案8】:

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

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

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

【讨论】:

一个简单的方法:harry.plus/blog/install-php-7-4-zts-with-ext-parallel-in-ubuntu【参考方案9】:

使用原生 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 这样做。)

【讨论】:

以上是关于PHP 中的并行处理 - 你是怎么做的?的主要内容,如果未能解决你的问题,请参考以下文章

使用 zeroMQ 在 PHP 中进行并行处理

《并行计算》期末总结

PHP多进程处理并行处理任务实例

PHP多进程处理并行处理任务实例(转,备用)

您好有个问题请教一下cpu怎么和gpu并行计算处理好日常软件

LCD怎么判断串行还是并行