Thanks to Douglas and Jakub for pointing me at the appropriate part of the PSGI spec for streaming.
Miyagawa mentions that plack streaming can be blocking, and running this with Starman demonstrates that nicely.
my $app = sub { my $env = shift; return sub { my $respond = shift; my $writer = $respond->([200, ['Content-Type', 'text/html']]); for (1..5) { my $dt = localtime; $writer->write("[ $dt ]
\n"); sleep 2; } $writer->close(); }; };
Starman attributes include:
'psgi.streaming' => 1, 'psgi.nonblocking' => ''
Firing off two requests at the same time, the second doesn’t start until the first completes.
[ Mon May 30 18:56:16 2011 ] [ Mon May 30 18:56:18 2011 ] [ Mon May 30 18:56:20 2011 ] [ Mon May 30 18:56:22 2011 ] [ Mon May 30 18:56:24 2011 ]
[ Mon May 30 18:56:26 2011 ] [ Mon May 30 18:56:28 2011 ] [ Mon May 30 18:56:30 2011 ] [ Mon May 30 18:56:32 2011 ] [ Mon May 30 18:56:34 2011 ]
Advertisements
Yes, that is correct. Most frameworks operate this way, only processing one request per interpreter instance. They can both count at the same time if you use a blocking server is in server pool configuration. Then when one of the sub processes are blocking on the counter, there are other sub processes ready to handle new requests in the pool and one can take the second request. I started my own local Starman instance of your script and saw the same behavior as you, but if I used two different browsers, I saw the behavior you were looking for: the timers overlapping. It looks like the browser is that is causing the issue (note: I was using Firefox 4; using IE 9 for both requests worked, so it is something in FF 4).
But, it really sounds like you are looking for is a non-blocking server. If you do not what to use threading, then the next best thing you can do is event-based. Twiggy is great for this, though you cannot use blocking code in this server, so it may not fit well without rewriting a lot of code. Your example for Twiggy using AnyEvent is here: https://gist.github.com/608ede892f073a3045f3
Hi Doug,
Thanks for the double-check. Interesting that different browsers behave differently. I only tested with FF3.
I don’t think Twiggy would work for my purposes. In my posts, I generally use sleep n to stand in for a procedure that takes a while to run. Maybe it calls out to the database, maybe it performs a CPU intensive task. I appreciate the example of how to schedule things with Twiggy (well, with AnyEvent really I guess from a quick glance at the code).
Ah, yes, I see. Since most of all Perl 5 code is already written in a blocking manner as it is, it is indeed difficult to take an event-based approach. If you are intended to be running some long-running code in your web server, then it would be best to use a preforking server like Starman, but you may want to increase the number of workers in your pool depending on your traffic, as a worker that is running the long process cannot accept other requests.
It will run both requests at the same time, if you have sufficient workers available. Alternatively, you can use corona rather then starman and add a “use Coro::Timer qw(sleep);” to the top of your psgi and the two requests will stream simultaneously in a single process.
Hi Winter,
I should have had plenty of workers available. I started starman with the default workers (5) and was only running two queries (albeit from the same firefox instance). It looks like firefox is doing something unusual per Doug above.
Also, I couldn’t use my usual telnet 8080 / GET / debug method as it gets timed out. Looks like Twiggy / Starman are a bit more strict on the format of the request than I’m used to with apache.
And as I mentioned to Doug, I don’t really have a useful web app that sleeps for 2 seconds. I’m using the sleep to simulate doing real work, so the Coro::Timer sleep isn’t quite what I’m looking for.