-
-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
StreamedResponse
not streaming with swoole integration, when using a symfony/http-kernel
-based application
#115
Comments
My approach to disable the # Disables symfony's internal response streaming, allowing for downstream
# consumers of the HTTP kernel to stream the response as they prefer instead.
streamed_response_listener: '@Core\Infrastructure\Symfony\HttpKernel\EventListener\DisableStreamedResponseListener'
Core\Infrastructure\Symfony\HttpKernel\EventListener\DisableStreamedResponseListener:
decorates: streamed_response_listener <?php
declare(strict_types=1);
namespace Core\Infrastructure\Symfony\HttpKernel\EventListener;
use Symfony\Component\HttpKernel\EventListener\StreamedResponseListener;
/**
* The original {@see StreamedResponseListener} takes the response during kernel
* dispatch, and starts writing to the output buffer.
*
* When using `ext-openswoole`, we do not want this behavior, as it breaks streaming
* of data between OpenSwoole workers, and the main OpenSwoole server.
*
* This decorator exists so it can be used to replace the behavior of the
* {@see StreamedResponseListener}, when configured within the DIC
*/
final class DisableStreamedResponseListener extends StreamedResponseListener
{
/**
* Disables all events we're listening to, by design
*/
public static function getSubscribedEvents(): array
{
return [];
}
} |
…ng data to swoole worker output This change ensures that, when converting a `StreamedResponse` to output within `php-runtime/swoole`, the output is streamed chunk-by-chunk, instead of being buffered in its entirety, and then being sent out to the swooler server all in one shot. This should fix some obvious performance, latency and memory issues related to streaming common responses assembled by PHP-side processes. Note: you will still need to disable symfony's `StreamedResponseListener` when working with this package. Ref: php-runtime#115
…ng data to swoole worker output This change ensures that, when converting a `StreamedResponse` to output within `php-runtime/swoole`, the output is streamed chunk-by-chunk, instead of being buffered in its entirety, and then being sent out to the swooler server all in one shot. This should fix some obvious performance, latency and memory issues related to streaming common responses assembled by PHP-side processes. Note: you will still need to disable symfony's `StreamedResponseListener` when working with this package. Ref: php-runtime#115
Potential fix at #116 |
The https://github.com/symfony/symfony-standard/blob/v2.0.25/web/app.php That was changed in the next release https://github.com/symfony/symfony-standard/blob/v2.1.0/web/app.php So from my point of view the Current workaround could be in your project using a compilerpass in your Kernel.php removing the service so you don't need to overwrite it in your services.yaml: class Kernel extends BaseKernel implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
$container->removeDefinition('streamed_response_listener');
}
} I personally would also prefer that the service definition will be removed from symfony itself, or set it to a state where it does nothing in its default setting. |
Dunno, some integration tests fail for me since I disabled it, but I'll check on my end when I have time. |
Maybe @nicolas-grekas or @fabpot could tell us about the need of the |
Good news seems like it looks good the |
…rves no purpose anymore (nicolas-grekas) This PR was merged into the 6.1 branch. Discussion ---------- [HttpKernel] Deprecate StreamedResponseListener, it serves no purpose anymore | Q | A | ------------- | --- | Branch? | 6.1 | Bug fix? | no | New feature? | no | Deprecations? | yes | Tickets | - | License | MIT | Doc PR | - `StreamedResponseListener` has been introduced at the same time as `StreamedResponse` in #2935. Its purpose was to make catching exceptions easier by wrapping the call to `$response->send()` in the main try/catch of `HttpKernel`. Since #12998, we have `HttpKernel::terminateWithException()`, and we don't need that anymore, so we can just remove the listener. This will help [integrate Symfony into e.g. Swoole](php-runtime/runtime#115) /cc @alexander-schranz. Commits ------- ee61774 [HttpKernel] Deprecate StreamedResponseListener, it serves no purpose anymore
…swoole worker output (#116) * Fix #115 - buffer each `StreamedResponse` chunk when sending data to swoole worker output This change ensures that, when converting a `StreamedResponse` to output within `php-runtime/swoole`, the output is streamed chunk-by-chunk, instead of being buffered in its entirety, and then being sent out to the swooler server all in one shot. This should fix some obvious performance, latency and memory issues related to streaming common responses assembled by PHP-side processes. Note: you will still need to disable symfony's `StreamedResponseListener` when working with this package. Ref: #115 * Disabled error handler replacement, which is implicitly done by the `GenericRuntime` We don't want to replace the error handler: PHPUnit is good enough. This should bring the tests back to green too. * Upgrade PHPUnit configuration to comply with latest `phpunit.xsd` Ref: https://github.com/sebastianbergmann/phpunit/blob/f61e897412fe831831462c0698a6208f7d8298f0/phpunit.xsd#L255 Ref: https://github.com/sebastianbergmann/phpunit/blob/f61e897412fe831831462c0698a6208f7d8298f0/phpunit.xsd#L16-L20
@Ocramius thanks for the workaround. You have literally saved me hours on troubleshooting why returning a file from an object storage via streamed response was malformed under ApiPlatform running via RoadRunner (runtime/roadrunner-symfony-nyholm). I saw your issue roadrunner-server/roadrunner#923 which lead me to this one. Are there any side effects by disabling the listener? |
So far, none that I could notice |
@jelovac they removed the listener also in symfony core for newer versions: symfony/symfony#45476 |
It seems that I have found one side effect. By disabling StreamedResponseListener integration tests which fetched streamed response are now failing. I think this has something to do with how phpunit bootstraps ApiPlatform application when performing integration tests using their src/Bridge/Symfony/Bundle/Test/ApiTestCase.php class. Will investigate tomorrow further. |
Couldn't figure it out so I added a workaround for the test environment based on your example:
This now satisfies both use cases. Lets hope this won't haunt me :) |
I have been playing around with the Symfony StreamedResponse as well and tried to find a solution for the problem with the buffer size that @Ocramius is describing as well. In my opinion a simple and BC friendly solution would be to pass a stream target as first parameter to the callback of the Symfony response so that it becomes something like this:
This would also make async responses possible:
|
While investigating the suitability of
runtime/swoole:0.3.0
, I ran into a blocker caused by streamed responses.I was evaluating
runtime/swoole
because I ran into a problem similar to #50, and as of roadrunner-server/roadrunner#923 (comment), I also couldn't use RoadRunner (which I much prefer to Swoole, at first glance).A controller producing large, slow responses
Testing this in automation is a bit of a mess (suggestions welcome though), but the idea is that I can stream a response as follows:
First problem:
Symfony\Component\HttpKernel\EventListener\StreamedResponseListener
When interacting with this controller, through
runtime/swoole
, the respone is not sent via\Runtime\Swoole\SymfonyHttpBridge
, where it should happen:runtime/src/swoole/src/SymfonyHttpBridge.php
Lines 48 to 57 in 420d39e
In this block,
$response->write($buffer);
should receive a massive chunk of111111...22222...<SNIP>...55555
, but instead, all output is sent to STDOUT in the worker (not to the response object), and a warning is produced:Effectively, this write produces nothing, because the response was already sent by
symfony/http-kernel
:runtime/src/swoole/src/SymfonyHttpBridge.php
Line 50 in 420d39e
After some investigation, I found that the culprit is that
symfony/http-kernel
sendsStreamedResponse
through a listener:https://github.com/symfony/symfony/blob/82e8d23788940421e0ad6e30163242db3ba27a02/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php#L27-L51
I disabled this listener by monkey-patching (while trying out stuff), but if you know a "clean" way to remove it completely from my application, lemme know.
Second problem: response buffer is completely sent in one shot
Assuming we disabled the
StreamedResponseListener
(clean solution pending), the response content now makes it toSwoole\Http\Response#write($buffer);
, but in one big chunk: the HTTP client sees the first byte when swoole finished collecting the whole response.This is because of this
ob_start()
call not having a defined buffer size specified:runtime/src/swoole/src/SymfonyHttpBridge.php
Lines 49 to 53 in 420d39e
According to the documentation for
ob_start()
:Given that PHP uses a default buffer size of
4096
, perhaps it would be a good idea to add one here too? I tried it locally, and my HTTP client starts receiving bytes much earlier than 5 seconds, this way :)The text was updated successfully, but these errors were encountered: