thelevti / phpfork
PHP进程分叉库。
5.0.0
2020-03-28 19:20 UTC
Requires
- php: ^7.2.0
- symfony/event-dispatcher: ^4.0.0 || ^5.0.0
Requires (Dev)
- ext-pcntl: *
- ext-posix: *
- ext-shmop: *
- friendsofphp/php-cs-fixer: ^2.16
- phan/phan: ^2.6
- phpstan/phpstan: ^0.12.18
- phpunit/phpunit: ^8.5
- squizlabs/php_codesniffer: ^3.5
- symfony/var-dumper: ^5.0
Suggests
- ext-pnctl: To allow this library forking processes.
- ext-posix: To allow this library getting process information.
- ext-shmop: To allow this library doing inter-process communication.
This package is auto-updated.
Last update: 2024-09-06 17:04:04 UTC
README
一个简单的库,使进程分叉尽可能简单。
thelevti/phpfork遵循语义版本。更多关于semver.org。
要求
安装
Composer
要在composer中使用此库,请在您的存储库根目录中运行以下终端命令。
composer require "thelevti/phpfork"
使用
此库使用命名空间TheLevti\phpfork。
示例:基本进程分叉
<?php use TheLevti\phpfork\Fork; use TheLevti\phpfork\ProcessManager; use TheLevti\phpfork\SharedMemory; $manager = new ProcessManager(); $fork = $manager->fork(function (SharedMemory $shm) { // Do something in a forked process! return 'Hello from ' . posix_getpid(); })->then(function (Fork $fork) { // Do something in the parent process when the fork is done! echo "{$fork->getPid()} says '{$fork->getResult()}'\n"; }); $manager->wait();
示例:将图片上传到CDN
将迭代器输入到进程管理器中,它将工作分成多个批次,并分散到多个进程上。
<?php use TheLevti\phpfork\ProcessManager; use SplFileInfo; $files = new RecursiveDirectoryIterator('/path/to/images'); $files = new RecursiveIteratorIterator($files); $manager = new ProcessManager(); $batchJob = $manager->process($files, function(SplFileInfo $file) { // upload this file }); $manager->wait();
示例:与Doctrine DBAL一起工作
当与数据库连接一起工作时,存在有关父/子进程的已知问题。有关pcntl_fork的php文档。
当分叉时,MySQL "查询期间丢失连接"问题的原因是子进程继承了父进程的数据库连接。当子进程退出时,连接关闭。如果此时父进程正在执行查询,它将在已关闭的连接上执行,因此产生错误。
这意味着在我们的示例中,我们将看到父进程中抛出的SQLSTATE[HY000]: General error: 2006 MySQL server has gone away异常。
针对这种情况的一个解决方案是使用PRE_FORK事件在分叉之前强制关闭DB连接。
<?php use Doctrine\DBAL\DriverManager; use TheLevti\phpfork\Batch\Strategy\ChunkStrategy; use TheLevti\phpfork\EventDispatcher\Events; use TheLevti\phpfork\EventDispatcher\SignalEventDispatcher; use TheLevti\phpfork\ProcessManager; $params = array( 'dbname' => '...', 'user' => '...', 'password' => '...', 'host' => '...', 'driver' => 'pdo_mysql', ); $forks = 4; $dataArray = range(0, 15); $callback = function ($value) use ($params) { // Child process acquires its own DB connection $conn = DriverManager::getConnection($params); $conn->connect(); $sql = 'SELECT NOW() AS now'; $stmt = $conn->prepare($sql); $stmt->execute(); $dbResult = $stmt->fetch(); $conn->close(); return ['pid' => getmypid(), 'value' => $value, 'result' => $dbResult]; }; // Get DB connection in parent $parentConnection = DriverManager::getConnection($params); $parentConnection->connect(); $dispatcher = new SignalEventDispatcher(); $dispatcher->addListener(Events::PRE_FORK, function () use ($parentConnection) { $parentConnection->close(); }); $manager = new ProcessManager($dispatcher, null, true); /** @var TheLevti\phpfork\Fork $fork */ $fork = $manager->process($dataArray, $callback, new ChunkStrategy($forks)); $manager->wait(); $result = $fork->getResult(); // Safe to use now $sql = 'SELECT NOW() AS now_parent'; $stmt = $parentConnection->prepare($sql); $stmt->execute(); $dbResult = $stmt->fetch(); $parentConnection->close();