-
Notifications
You must be signed in to change notification settings - Fork 576
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
Add streaming proxy feature #1373
Conversation
The core functionality seems pretty solid, and so do the new low level methods and attributes (naming could use some bikeshedding probably). What i do not quite like yet is the API of the helper, but i can't think of a better alternative. I originally planned to use the returned promise for response rewriting (adding headers and so on). Unfortunately the promise would have to be resolved right in between the The helper API might be a blocker for this feature. |
This script illustrates the problem. use Mojo::Base -strict;
use Mojo::EventEmitter;
use Mojo::IOLoop;
use Mojo::Promise;
use Mojo::Util 'dumper';
my $promise = Mojo::Promise->new;
my $events = Mojo::EventEmitter->new;
# We want to use a promise to change the "foo" value
my $response = {foo => 'before'};
$promise->then(sub {
$response->{foo} = 'after';
});
# The first event resolves the promise, and the second prints the "foo" value
$events->on(
one => sub {
$promise->resolve();
}
);
$events->on(
two => sub {
say dumper $response;
}
);
# Events happen in the right order, but the promise only resolves after the
# second event because it requires a reactor tick
$events->emit('one')->emit('two');
Mojo::IOLoop->one_tick; |
Having mentioned the negative, on the plus side the streaming proxy has been up to 30 times faster than previously used proxy implementations for Mojolicious. use Mojolicious::Lite;
my $UPSTREAM = 'http://127.0.0.1:3000';
# Old way (caching the whole response)
get '/proxy1' => sub {
my $c = shift;
$c->ua->get_p($UPSTREAM)->then(sub {
my $tx = shift;
$c->tx->res($tx->res);
$c->rendered;
});
};
# New way (streaming the response as chunks of data arrive)
get '/proxy2' => sub {
my $c = shift;
$c->proxy->get_p($UPSTREAM);
};
app->start;
And response size does not matter at all, it can even be an infinite EventSource stream. Throttling works automatically, so if one direction is faster than the other the proxy will not cache more than 1MB of data (this is configurable).
This test was performed with 1MB responses from another Mojolicious application on a Linux machine with the page cache disabled, to simulate a worst case scenario. |
I'm calling for a vote on this @mojolicious/core. |
This PR also contains some small optimizations for streaming responses in general. Found those bottlenecks while benchmarking the proxy helper. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I apologise in advance if my questions are idiotic. Please ignore me if that is the case.
I'm very much positive to the change in general and I more than welcome a simple proxy helper.
Ok, the problem with the API has been resolved, this should be fine now. 😀 |
ed9db29
to
add9d83
Compare
The solution we settled on for the API problem was to just reuse the my $tx = $c->ua->build_tx(GET => 'http://mojolicious.org');
$c->proxy->start_p($tx);
$tx->res->content->once(body => sub {
$c->res->headers->header('X-Proxy' => 'Mojo');
}); This gives us all the flexibility we need for rewriting HTTP responses. |
Since there is a lot of potential for bugs, i suggest we keep the feature experimental for some time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the code makes sense and on a high level I'm giving the PR 👍
331d0b7
to
a11a493
Compare
Looks good to me 👍 . For posterity I want to leave a note that reminds us to document the timing (and values if any) of the resolution/rejection of the promise. For now, that can be deferred. |
This is an implementation of the streaming proxy described in #1295.