snidel
一个多进程容器。 snidel使所有PHP开发人员更容易在没有任何扩展的情况下进行并行处理。
请考虑向该项目的作者Akihito Nakano捐款,以表达您的❤️和支持。
赞助商@ackintosh在Github赞助商上
snidel解决了什么?
(en)
没有几个人,使用PHP开始他们的编程运营商,然后继续。并行处理,他们不熟悉它,可能是他们的障碍。
否则,那些不得不使用PHP的语言开发的人(例如一种具有较高功能的语言用于并行处理)。 (过去是我。)
为了使并行处理更轻松,本能地使用它们,我开始开发snidel 。
当您考虑“如何并行?”时, snidel可能是您的选择之一。这对我来说是一种荣幸。
(JA)
我认为有些程序员已经开始使用PHP编程,并通过PHP(我)建立了职业生涯。对于这样的人来说,并行处理可能不熟悉它,或者似乎是一个高阈值。
另外,有些人必须在某种情况下不受PHP以外的其他语言(例如,由于各种情况而具有出色机制的语言)进行开发(例如,我以前是这种情况)。
我们已经开始开发snidel ,目的是使使用并行处理更轻松,直观地解决问题。
我希望snidel可以成为您的选择之一,如果您说:“我想并行执行此过程,我该怎么办?”
通过作曲家安装snidel
$ composer require ackintosh/ snidel :~0.11.0
建筑学
好处
也可能通过建立功能(例如exec )进行并行处理:
initialize_data_required_for_the_slow_jobs (); exec ( \' php slow_job1.php & \' ); exec ( \' php slow_job2.php & \' );
对于以上感到“痛苦”的开发人员, snidel可以提供相当不错的体验,并会简化其PHP编程。
我们将浏览用法,以显示snidel如何并行处理您的编程。使用snidel的经验应解决您的痛苦。让我们开始吧!
用法
基本用法
snidel;
$f = function ($s) {
sleep(3);
echo \’echo: \’ . $s;
return \’return: \’ . $s;
};
$s = time();
$ snidel = new snidel ();
$ snidel ->process($f, [\’foo\’]);
$ snidel ->process($f, [\’bar\’]);
$ snidel ->process($f, [\’baz\’]);
// ` snidel ::results()` returns `\\Generator`
foreach ($ snidel ->results() as $r) {
// string(9) \”echo: foo\”
var_dump($r->getOutput());
// string(11) \”return: foo\”
var_dump($r->getReturn());
}
// If you don\’t need the results, let\’s use ` snidel ::wait()` instead of ` snidel ::results()`
// $ snidel ->wait();
echo (time() – $s) . \’sec elapsed\’ . PHP_EOL;
// 3sec elapsed.\”>
<?php use Ackintosh \\ snidel ; $ f = function ( $ s ) { sleep ( 3 ); echo \' echo: \' . $ s ; return \' return: \' . $ s ; }; $ s = time (); $ snidel = new snidel (); $ snidel -> process ( $ f , [ \' foo \' ]); $ snidel -> process ( $ f , [ \' bar \' ]); $ snidel -> process ( $ f , [ \' baz \' ]); // ` snidel ::results()` returns `\\Generator` foreach ( $ snidel -> results () as $ r ) { // string(9) \"echo: foo\" var_dump ( $ r -> getOutput ()); // string(11) \"return: foo\" var_dump ( $ r -> getReturn ()); } // If you don\'t need the results, let\'s use ` snidel ::wait()` instead of ` snidel ::results()` // $ snidel ->wait(); echo ( time () - $ s ) . \' sec elapsed \' . PHP_EOL ; // 3sec elapsed.
构造函数参数
所有参数都是可选的。
snidel([
\’concurrency\’ => 3,
// Please refer to `Logging`
\’logger\’ => $monolog,
// Please refer to `Using custom queue`
\’driver\’ => $driver,
// a polling duration(in seconds) of queueing
\’pollingDuration\’ => 1,
]);\”>
new snidel ([ \' concurrency \' => 3 , // Please refer to `Logging` \' logger \' => $ monolog , // Please refer to `Using custom queue` \' driver \' => $ driver , // a polling duration(in seconds) of queueing \' pollingDuration \' => 1 , ]);
与call_user_func_array相同的参数
snidel->process($f, [\’arg1\’, \’arg2\’]);
// global function
$ snidel ->process(\’myfunction\’);
// instance method
$ snidel ->process([$instance, \’method\’]);
\”>
// multiple arguments $ snidel -> process ( $ f , [ \' arg1 \' , \' arg2 \' ]); // global function $ snidel -> process ( \' myfunction \' ); // instance method $ snidel -> process ([ $ instance , \' method \' ]);
标记任务
snidel->process($f, \’arg-A_tag1\’, \’tag1\’);
$ snidel ->process($f, \’arg-B_tag1\’, \’tag1\’);
$ snidel ->process($f, \’arg_tag2\’, \’tag2\’);
foreach ($ snidel ->results as $r) {
// `Task::getTag()` returns the tag passed as 3rd parameter of ` snidel ::process()`
switch ($r->getTask()->getTag()) {
case \’tag1\’:
$r->getReturn(); // arg-A_tag1 | arg-B_tag1
break;
case \’tag2\’:
$r->getReturn(); // arg_tag2
break;
default:
$r->getReturn();
break;
}
}\”>
$ f = function ( $ arg ) { return $ arg ; }; $ snidel -> process ( $ f , \' arg-A_tag1 \' , \' tag1 \' ); $ snidel -> process ( $ f , \' arg-B_tag1 \' , \' tag1 \' ); $ snidel -> process ( $ f , \' arg_tag2 \' , \' tag2 \' ); foreach ( $ snidel -> results as $ r ) { // `Task::getTag()` returns the tag passed as 3rd parameter of ` snidel ::process()` switch ( $ r -> getTask ()-> getTag ()) { case \' tag1 \' : $ r -> getReturn (); // arg-A_tag1 | arg-B_tag1 break ; case \' tag2 \' : $ r -> getReturn (); // arg_tag2 break ; default : $ r -> getReturn (); break ; } }
记录
snidel支持使用Logger进行记录,该记录器实现PSR-3:Logger接口。
snidel ([\’logger\’ => $monolog]);
$ snidel ->process($f);
// 2017-03-22 13:13:43 > DEBUG > forked worker. pid: 60018 {\”role\”:\”master\”,\”pid\”:60017}
// 2017-03-22 13:13:43 > DEBUG > forked worker. pid: 60019 {\”role\”:\”master\”,\”pid\”:60017}
// 2017-03-22 13:13:43 > DEBUG > has forked. pid: 60018 {\”role\”:\”worker\”,\”pid\”:60018}
// 2017-03-22 13:13:43 > DEBUG > has forked. pid: 60019 {\”role\”:\”worker\”,\”pid\”:60019}
// 2017-03-22 13:13:44 > DEBUG > —-> started the function. {\”role\”:\”worker\”,\”pid\”:60018}
// 2017-03-22 13:13:44 > DEBUG > —-> started the function. {\”role\”:\”worker\”,\”pid\”:60019}
// …
\”>
// e.g. MonoLog use Monolog \\ Formatter \\ LineFormatter ; use Monolog \\ Handler \\ StreamHandler ; use Monolog \\ Logger ; $ monolog = new Logger ( \' sample \' ); $ stream = new StreamHandler ( \' php://stdout \' , Logger:: DEBUG ); $ stream -> setFormatter ( new LineFormatter ( \" %datetime% > %level_name% > %message% %context% \\n\" )); $ monolog -> pushHandler ( $ stream ); $ snidel = new snidel ([ \' logger \' => $ monolog ]); $ snidel -> process ( $ f ); // 2017-03-22 13:13:43 > DEBUG > forked worker. pid: 60018 {\"role\":\"master\",\"pid\":60017} // 2017-03-22 13:13:43 > DEBUG > forked worker. pid: 60019 {\"role\":\"master\",\"pid\":60017} // 2017-03-22 13:13:43 > DEBUG > has forked. pid: 60018 {\"role\":\"worker\",\"pid\":60018} // 2017-03-22 13:13:43 > DEBUG > has forked. pid: 60019 {\"role\":\"worker\",\"pid\":60019} // 2017-03-22 13:13:44 > DEBUG > ----> started the function. {\"role\":\"worker\",\"pid\":60018} // 2017-03-22 13:13:44 > DEBUG > ----> started the function. {\"role\":\"worker\",\"pid\":60019} // ...
儿童的错误信息
snidel->process(function ($arg1, $arg2) {
exit(1);
}, [\’foo\’, \’bar\’]);
$ snidel ->get();
var_dump($ snidel ->getError());
// class Ackintosh\\ snidel \\Error#4244 (1) {
// …
// }
foreach ($ snidel ->getError() as $pid => $e) {
var_dump($pid, $e);
}
// int(51813)
// array(5) {
// \’status\’ => int(256)
// \’message\’ => string(50) \”an error has occurred in child process.
// \’callable\’ => string(9) \”*Closure*\”
// \’args\’ =>
// array(2) {
// [0] => string(3) \”foo\”
// [1] => string(3) \”bar\”
// }
// \’return\’ => NULL
// }
// }\”>
$ snidel -> process ( function ( $ arg1 , $ arg2 ) { exit ( 1 ); }, [ \' foo \' , \' bar \' ]); $ snidel -> get (); var_dump ( $ snidel -> getError ()); // class Ackintosh\\ snidel \\Error#4244 (1) { // ... // } foreach ( $ snidel -> getError () as $ pid => $ e ) { var_dump ( $ pid , $ e ); } // int(51813) // array(5) { // \'status\' => int(256) // \'message\' => string(50) \"an error has occurred in child process. // \'callable\' => string(9) \"*Closure*\" // \'args\' => // array(2) { // [0] => string(3) \"foo\" // [1] => string(3) \"bar\" // } // \'return\' => NULL // } // }
使用自定义队列
snidel取决于Bernard作为队列抽象层。伯纳德(Bernard)是一个多余的PHP库,用于创建背景作业以供以后处理。
默认情况下, snidel构建了Flatfile驱动程序,但是从种族条件的角度来看,我们建议在生产中使用更可靠的队列。
Amazon SQS
snidel([
\’driver\’ => $driver,
]);\”>
$ connection = Aws \\ Sqs \\SqsClient:: factory ([ \' key \' => \' your-aws-access-key \' , \' secret \' => \' your-aws-secret-key \' , \' region \' => \' the-aws-region-you-choose \' ]); $ driver = new Bernard \\ Driver \\ SqsDriver ( $ connection ); new snidel ([ \' driver \' => $ driver , ]);
有关驾驶员的详细信息,请参阅此处。
文章
这是引入snidel的文章。谢谢你!
- [php-都
要求
- PCNTL功能
版本指导
| snidel | php |
|---|---|
| 0.1〜0.8 | > = 5.3 |
| 0.9〜 | > = 5.6 |
| 0.13 | > = 7.1 |
Docker
我们建议您尝试使用Docker,因为snidel需要需求中显示的一些PHP扩展。
在Docker容器中运行单元测试
snidel .
docker run –rm -v ${PWD}:/ snidel snidel php composer.phar install
docker run –rm -v ${PWD}:/ snidel snidel vendor/bin/phpunit\”>
curl -Ss https://get*comp**oser.org/installer | php docker build -t snidel . docker run --rm -v ${PWD} :/ snidel snidel php composer.phar install docker run --rm -v ${PWD} :/ snidel snidel vendor/bin/phpunit
作者
snidel ©Ackintosh,根据MIT许可证发布。
由Ackintosh撰写和维护
作者关于snidel (JA)的博客条目:
- https://ackintosh.g*ith**ub.io/blog/2015/09/29/snidel/
- https://ackintosh.g**i*thub.io/blog/2015/11/08/snidel_0_2_0/
- https://ackintosh.g**it*hub.io/blog/2016/04/04/snidel_0_4_0/
- https://ackintosh.gi***thub.io/blog/2016/04/04/snidel_0_5_0/
- https://ackintosh.**gi*thub.io/blog/2016/05/04/snidel_0_6_0/
- https://ackintosh.gi**th*ub.io/blog/2016/09/09/snidel_0_7_0/
- https://ackintosh.g*i*thu*b.io/blog/2017/03/10/snidel_0_8_0/
- https://ackintosh.g**it*hub.io/blog/2017/07/17/snidel_0_9_0/
致谢
感谢Jetbrains提供免费的开源许可证。
