Closed sxnazirov closed 4 years ago
Thanks for author! Can this be used to host a Yii 2.x web app?
Unfortunately, all Yii versions not supporting PSR-7. Maybe in Yii 3 but I checked a few days ago and it looks like no support in it neither.
3.x supports PSR-7: https://github.com/yiisoft/yii-web/blob/master/composer.json#L68
3.x supports PSR-7: https://github.com/yiisoft/yii-web/blob/master/composer.json#L68
@samdark How I can start the app with PSR request? Do you have example?
By calling $app->handleRequest($request).
By calling $app->handleRequest($request).
Ok. Will try within next few days. If it works @wolfy-j will add a wiki page.
For Yii2 I have a very early working PSR-7 bridge I've been working on for a while that bridges a Psr\Http\Message\ServerRequestInterface
to yii\web\Request
and yii\web\Response
back to a Psr\Http\Message\ResponseInterface
for consumption by PSR-7 servers like RoadRunner.
It's available at: https://github.com/charlesportwoodii/yii2-psr7-bridge
At the moment it can mostly run the yii2-basic-app
without any issues, but there's still a few things that need to be worked out on it. Pull requests are welcome to improve it.
@charlesportwoodii have you tried to bench with keep-alive on?
@wolfy-j Yes and no.
siege -R <(echo connection = keep-alive)
burns through sockets when keep-alive
is configured, which causes siege to abort the test only after a few seconds. Since the metrics gathered were mainly to determine if the work-effort was worth pursuing or not, I didn't dive in much further since I was testing in non real-world conditions (both siege and rr running on the same machine, testing over lo). For real benchmarks I'd probably test against a remote server and with a few other tools.
Once the library is a bit more complete I plan on doing some some actual benchmarking in real-world conditions.
Update: i tried to connect RR to Yii3. There some problems with integration. I already contacted @samdark about them.
This is worker file for Yii3 is my fix (@https://github.com/yiisoft/yii-web/pull/40) will be merged:
<?php
require __DIR__ . '/vendor/autoload.php';
use hiqdev\composer\config\Builder;
use yii\di\Container;
use yii\helpers\Yii;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
$relay = new Spiral\Goridge\StreamRelay(STDIN, STDOUT);
$psr7 = new Spiral\RoadRunner\PSR7Client(new Spiral\RoadRunner\Worker($relay));
$httpFoundationFactory = new HttpFoundationFactory();
while ($req = $psr7->acceptRequest()) {
try {
$container = new Container(require Builder::path('web'));
Yii::setContainer($container);
/** @var \yii\web\Application $app */
$app = $container->get('app');
$request = $httpFoundationFactory->createRequest($req);
/** @var \yii\web\Request $yiiReq */
$yiiReq = $container->get('request');
$yiiReq->setMethod($request->getMethod());
$yiiReq->setUri($req->getUri());
$yiiReq->setServerParams($request->server->all());
$yiiReq->setProtocolVersion($request->getProtocolVersion());
$yiiReq->setCookieParams($req->getCookieParams());
$yiiReq->setQueryParams($request->query->all());
$yiiReq->setAttributes($request->attributes->all());
$yiiReq->setParsedBody($req->getParsedBody());
$req->getBody()->rewind();
$yiiReq->setBody($req->getBody());
$container->set('request', $yiiReq);
$response = $app->handleRequest($yiiReq);
$psr7->respond($response->send(true));
} catch (\Throwable $e) {
$psr7->getWorker()->error((string)$e);
}
}
@wolfy-j @sxnazirov Small update - so, the actual problem with Yii3 is much deeper than i expected. I recommend closing this issue for now with resolution "Not possible at this moment, ETA unavailable". I keep working on pull requests to Yii3 to fix some fundamental problems that prevent us from using it with RR.
As of Yii2 - i believe it will have the same problem with resetting data between requests. @charlesportwoodii we probably need to talk in order to verify your solution. As i know Yii2 don't have terminate process as well.
Tech info: At this moment Yii using too much direct strout printing. I'm working on rewriting request handling and error processing (at this moment most of the error writes to stdout, not response). As the second problem that right now on in backlog - terminating request. At this moment Yii doesn't have any proper termination sequence, so data between requests will be not reset.
Agreed, we can come back to this issue once Yii3 is released. Current answer is - NO, it's not possible.
@Alex-Bond is working on it for 3.0.
@Alex-Bond
For the Yii2 bridge I mentioned, the issues you mentioned (Yii2's error handler and resetting the application) are solved.
The main loop with the bridge presently looks as follows:
while ($request = $psr7->acceptRequest()) {
try {
// The application state is automatically reset at start of each request. `handle` also calls `\yii\Psr7\web\Application::terminate()` to flush logs and detach Yii2's global events.
$response = $application->handle($request);
$psr7->respond($response);
} catch (\Throwable $e) {
$psr7->getWorker()->error((string)$e);
}
// Automatically stop the worker if it approaches 90% of the defined memory limit to avoid crashes due to memory leaks
if ($application->clean()) {
$psr7->getWorker()->stop();
return;
}
}
With the configuration looking like:
env:
YII_DEBUG: true
YII_ENV: dev
YII_ALIAS_WEBROOT: ./web/
YII_ALIAS_WEB: 'http://127.0.0.1:8080/'
http:
address: 0.0.0.0:8080
workers:
command: "php ./roadrunner"
pool:
numWorkers: 1
static:
# root directory for static file (http would not serve .php and .htaccess files).
dir: "web"
# list of extensions for forbid for serving.
forbid: [".php"]
I have a running checklist detailing the current progress at the bottom of https://github.com/charlesportwoodii/yii2-psr7-bridge. Excluding a few items such as bootstraped components and some session issues, everything else works for the most part. These issues are only a problem for web apps since you can work-around them in RESTful API's.
This bridge is still pretty early, but at least for Yii2 the problem seems solvable.
That's great for 2.0. Keep up digging. For 3.0 it's a good way to check if framework is ready to work in such mode.
@samdark
I think the only major issue is going to be around yii\web\Session
. Yii::$app->getSession->close()
doesn't seem to do the trick as I would expect. yii\web\CacheSession
or a custom session handler could probably solve that problem - I just haven't gotten there yet.
Aside from that, the only real "annoyances" right now are:
YII_BEGIN_TIME
is defined by BaseYii
, so the "request time" constantly increases. It's not really an issue since I can use a PSR-15 middleware to properly measure that, it's just annoying in yii2-debug
.memory_get_usage()
calls will report the memory usage of the worker, rather than the individual request. Again, more of annoyance with yii2-debug
than anything else.preInit
, Component::__construct($config)
and bootstrap()
. Performance could be improved and memory usage could be lowered if there was an easy way to re-use existing components and modules instead of re-initializing them. The memory usage is low enough though we can just tell RR to kill the worker if it gets within 10% or so of the max memory so it's not a deal breaker.yii-debug
binding the debug bar hundreds of times on the page). It would be nice to have a $app->off()
that de-registered every event associated to all components, or maybe a way to see all events that are attached to all objects for manual de-registration. Custom events that get registered are presently programmatically inaccessible. It would be up to the developer to know to de-register their custom events.Everything else either just works or I have a work-around for. I'll give another update once I'm a bit further along.
@charlesportwoodii that you for a clarification! I checked your code but didn't find how you reset database connections. Can you point me?
@Alex-Bond,
That's one of those not-yet-implemented but I have a plan for tasks. = )
It'll probably go here once I add it. Yii2 doesn't enable the db
component by default, and it can be named something other than db
, so I just need to search through the components for instances of yii/db/Connection
and manually call yii/db/Connection::close()
on them after I find them.
I'm going to highlight this ticket in README in case if someone else can help.
Minor updates:
\yii\db\Connection
instances are identified and closed at the end of each request: https://github.com/charlesportwoodii/yii2-psr7-bridge/blob/master/src/web/Application.php#L160.\yii\web\Session
to work with the PSR-7 response object and play nicely with ext/session
. All the normal extensions of yii\web\Session
should work out of the box since there aren't any custom classes involved in that.I also pushed out an instance of yii-app-basic with a sqlite user database for people to work with.
Functionally this is probably ~90% complete aside from test cases. There's a couple of small changes I'll work on in the next few days time pending. All the big ticket items seem to be working now with yii-app-basic and a separate JSON API I've been working with.
@charlesportwoodii, good job, you are is big lad!
@charlesportwoodii your method of checking connections assumes they are all components of the main application; they could easily be inside modules as well.
The issues you run into are approximately the same as they are in a testing framework (I've been rewriting the Yii2 Codeception connector on and off over the past year) A cleaner approach could be this one: https://github.com/Codeception/Codeception/blob/3.0/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php
It used an event based approach.
The rest of the application is reset here: https://github.com/Codeception/Codeception/blob/3.0/src/Codeception/Lib/Connector/Yii2.php#L107
Just in case it might be of use to you!
@SamMousa Good point on the database connections. I'll be sure to take a look at the reset Codeception uses to see if other items can be incorporated.
Thanks for the tip!
@SamMousa
Thanks again for the tip on the connections that I overlooked. I'm implemented a similar solution that closes database connections for associated modules.
For anyone else following along at home, I've also updated the project to include a better event handler that properly de-registers every event at the end of the event loop. Previously, events not manually off
ed would be duplicated until the process was finally terminated. This is fixed now - at least in the Yii2 App Basic package which I've updated. I plan on throwing a larger project like Craft3 which I had previous trouble with at it to see if it's fully resolved there so that even larger projects can take advantage of it.
@charlesportwoodii Yii 3 with Spiral should go way smoother because of PSR-7 and middleware. Validating that it works is on my too long TODO list :)
Yii 3 experiment: https://forum.yiiframework.com/t/using-roadrunner-as-a-server/127060
Closing this issue due to the inactivity. Please take a look at the upcoming Yii3 for the integrated RoadRunner support.
Thanks for author! Can this be used to host a Yii 2.x web app?