Closed cctjl closed 5 years ago
Thanks all. I want to know why it cannot use the autoloader of yii. I created one composer package, and in the package, I can use yii without any problems. So why it does not work in this amphp/parallel?
I tried add the class "TigerApi" to composer.json, then it reminded me "yii is not found" because TigerApi used yii. I think it's because amphp/parallel used the standard composer autoloader. How can I let amphp/parallel use the autoloader which is registered by yii?
I think it's reasonable that composer package use the same autoloaders as the main process. If it is possible, many php frameworks will be easy to use.
This library runs PHP code enqueued to a Worker in a separate process or thread (context). That context must be initialized like any other PHP script. This generally means including an autoloader. The composer autoloader is included automatically – and generally works for projects using only composer for package management.
This question has now come up often enough that I think it would be a good idea to add support for specifying another autoloader file to be included when the context is started.
https://github.com/amphp/parallel/commit/7c8756c3a59632c4480fa6b8c283b6c5430de770 in the ext-parallel
branch implements this with AutoloadingWorkerFactory
. You can set the global worker factory to use AutoloadingWorkerFactory
using the factory()
function in the Amp\Parallel\Worker
namespace.
Worker\factory(new AutoloadingWorkerFactory('/path/to/custom/autoload.php'));
Call this function before using enqueue
or enqueueCallable
.
Please give the ext-parallel
branch a try and let me know if AutoloadingWorkerFactory
works for you.
Thanks trowski. I tried and the same error found.
First git clone amp/parallel then git checkout to ext-parallel branch. I created a file CustomerApi.php and add the test function:
use Yii;
class CustomerApi
{
public static function testParallel()
{
$method = Yii::$app->request->getMethod();
return $method;
}
}
Then run it in a yii controller:
use Amp\Parallel\Worker;
use Amp\Promise;
use common\services\internal\CustomerApi;
$promises = [];
$autoloading_factory = new Worker\AutoloadingWorkerFactory(PROJECT_ROOT . '/vendor/yiisoft/yii2/Yii.php'); // this file is the same as yii used
Worker\factory($autoloading_factory);
$promises['address_settings'] = Worker\enqueueCallable([CustomerApi::class, 'testParallel']);
$result = Promise\wait(Promise\all($promises));
exit(var_dump($result));
Result is "Uncaught Error in worker with message \"Class 'common\services\internal\CustomerApi' not found\" and code \"0\"". I confirmed that the Worker\factory function received correct params. Yii version is 2.0.14.2.
Is CustomerApi
defined in a way that it is autoloadable by either the composer autoloader or the Yii autoloader?
Is
CustomerApi
defined in a way that it is autoloadable by either the composer autoloader or the Yii autoloader?
I think yii autoloader can find it. Because if I execute it directly in controller, it is correct. like this:
use common\services\internal\CustomerApi;
// CustomerApi.php is in common/services/internal/CustomerApi.php
CustomerApi::testParallel();
@cctjl Are you telling Yii to autoload classes from common\services\internal
somewhere in your code. I'm guessing that's what's missing from the worker.
I modified the ext-parallel
branch with this commit, renaming AutoloadingWorkerFactory
to BootstrapWorkerFactory
as I think this better represents what is being done here – including another file to setup the worker. @cctjl please run composer update
and update your code accordingly.
I did some quick testing with Yii and was able to make the following work.
<?php // bootstrap.php
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php';
Yii::setAlias('services', 'common/services');
<?php // test.php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php';
use Amp\Parallel\Worker;
use Amp\Promise;
use services\internal\CustomerApi;
Yii::setAlias('services', 'common/services');
Worker\factory(new Worker\BootstrapWorkerFactory(__DIR__ . '/bootstrap.php'));
$result = Promise\wait(Worker\enqueueCallable([CustomerApi::class, 'testParallel']));
var_dump($result);
<?php // common/services/internal/CustomerApi.php
namespace services\internal;
class CustomerApi
{
public static function testParallel()
{
return 'It worked!';
}
}
Running test.php
gives me the following:
string(10) "It worked!"
Define whatever aliases or other autoloading hooks you need in bootstrap.php
and use this file with BootstrapWorkerFactory
. Hopefully that will set up the worker so it can autoload classes from Yii.
I modified the
ext-parallel
branch with this commit, renamingAutoloadingWorkerFactory
toBootstrapWorkerFactory
as I think this better represents what is being done here – including another file to setup the worker. @cctjl please runcomposer update
and update your code accordingly.I did some quick testing with Yii and was able to make the following work.
<?php // bootstrap.php require __DIR__ . '/vendor/yiisoft/yii2/Yii.php'; Yii::setAlias('services', 'common/services');
<?php // test.php require __DIR__ . '/vendor/autoload.php'; require __DIR__ . '/vendor/yiisoft/yii2/Yii.php'; use Amp\Parallel\Worker; use Amp\Promise; use services\internal\CustomerApi; Yii::setAlias('services', 'common/services'); Worker\factory(new Worker\BootstrapWorkerFactory(__DIR__ . '/bootstrap.php')); $result = Promise\wait(Worker\enqueueCallable([CustomerApi::class, 'testParallel'])); var_dump($result);
<?php // common/services/internal/CustomerApi.php namespace services\internal; class CustomerApi { public static function testParallel() { return 'It worked!'; } }
Running
test.php
gives me the following:string(10) "It worked!"
Define whatever aliases or other autoloading hooks you need in
bootstrap.php
and use this file withBootstrapWorkerFactory
. Hopefully that will set up the worker so it can autoload classes from Yii.
Yes, You are right. It's because I called "setAlias" so we need a "bootstrap.php". Now we can use the function in yii, but the yii components is empty in parallel process so I cannot call "getMethod" on Yii::$app->request in "CustomerApi.php". It's enough for me. Thanks.
Since the worker is a new PHP environment, none of the request data will be available in the worker. Workers should be used for CPU-intensive or blocking tasks. You can pull the information you need from a request, then send only that information to a worker.
That being said, I'm not sure how much this component makes sense in a traditional PHP framework that runs in a web server SAPI. What were you hoping to accomplish?
Since the worker is a new PHP environment, none of the request data will be available in the worker. Workers should be used for CPU-intensive or blocking tasks. You can pull the information you need from a request, then send only that information to a worker.
That being said, I'm not sure how much this component makes sense in a traditional PHP framework that runs in a web server SAPI. What were you hoping to accomplish?
Sorry for I did not reply earlier and thanks for your help. You are right. I just found the fact and write it. I don't have any special meaning.. Thanks again for your help.
Hi, how can we pass context app from Yii2 console app to parallelMap function ? app\components\websocket\SocketServer ` public function eventLoop (){
DebugMessager::log( "Starting app event loop from ".getcwd());
//child process for workers pool
$pool = new DefaultPool();
$poolData = 'ConferenceChildProcess';
$promises = parallelMap(\range(1, 2), function($i) use ($poolData){
echo "Process #$i started in $poolData".PHP_EOL;
//define('AMQP_DEBUG', true);
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare($poolData, false, true, false, false);
echo " [*] Waiting for messages. To exit press CTRL+C\n";
$callback = function ($msg) {
echo ' [x] Received ', $msg->body, "\n";
sleep(2);
echo " [x] Done\n";
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
};
$channel->basic_qos(null, 1, null);
$channel->basic_consume($poolData, '', false, false, false, false, $callback);
while ($channel->is_consuming()) {
$channel->wait();
}
$channel->close();
$connection->close();
return 2;
}, $pool);
return Promise\wait($promises);`
causes Class 'app\components\websocket\SocketServer' not found in ..\src\vendor\opis\closure\src\SerializableClosure.php on line 263
I run codes like this:
The output:
Uncaught Error in worker with message \"Class 'common\\services\\internal\\TigerApi' not found\" and code \"0\"
How can I add the class to autoload? I used Yii2.0 and I tried the method: https://github.com/amphp/parallel/issues/55, but it appeared yii is not found.
I know that yii has it's owner autoload class, how can I let this package use the autoloader of yii?