sbs
sbs

Reputation: 1309

How do you implement non-blocking responses in a perl plack app?

I'm trying to write a a perl-based webserver using Twiggy::Server (which means a plack app). I want to respond to a request (coming from an ajax call on a webpage) for some data by running some possibly time-consuming subroutine which generates the data and then turns it into a JSON string for returning to the client webpage.

You can see a cut-down test version of my server here: http://pastebin.com/iNaDTVwL That example shows the problem I'm facing with my current implementation; using AnyEvent::ForkManager to do the non-blocking part of things results in truncation of the 'big' json response.

This document would answer my questions perfectly (and better explains what I'm trying to do): https://github.com/jjn1056/Example-PlackStreamingAndNonblocking ... if it was finished. I'm just missing the 'proper' way of doing non-blocking, instead of using AnyEvent::ForkManager which seems like a bit of a hack.

Upvotes: 0

Views: 958

Answers (2)

LeoNerd
LeoNerd

Reputation: 8542

Personally I'd use Net::Async::HTTP::Server::PSGI. From its SYNOPSIS:

use Net::Async::HTTP::Server::PSGI;
use IO::Async::Loop;

my $loop = IO::Async::Loop->new;

my $httpserver = Net::Async::HTTP::Server::PSGI->new(
   app => sub {
      my $env = shift;

      return [
         200,
         [ "Content-Type" => "text/plain" ],
         [ "Hello, world!" ],
      ];
   },
);

$loop->add( $httpserver );

$httpserver->listen(
   addr => { family => "inet6", socktype => "stream", port => 8080 },
   on_listen_error => sub { die "Cannot listen - $_[-1]\n" },
);

$loop->run;

Obviously this particularly tiny example doesn't demonstrate anything asynchronous, but you have full access to all of the IO::Async system in order to defer and respond later.

Upvotes: 1

Sobrique
Sobrique

Reputation: 53508

So following on from the comments - I don't know enough about the things you're using to give you a specific response, but can offer up something generic.

Using threads to 'async' part of your Perl script:

#!/usr/bin/perl

use strict;
use warnings;

use threads;
use Thread::Queue; 

my $input_q = Thread::Queue -> new();
my $success_q = Thread::Queue -> new(); 
my $failure_q = Thread::Queue -> new();

my $thread_count = 4; 

sub spinoff_thread {
    while ( my $target = $input_q -> dequeue() )
    {
       #do something to $target
       my @results = `ping -c 10 -i 1 $target`;
       if ( $? ) { 
           $failure_q -> enqueue ( $target );
       }
       else {
           $success_q -> enqueue ( $target );
       }
    } 
}

#main bit

for ( 1..$thread_count ) {
    my $thr = threads -> create ( \&spinoff_thread );
}

foreach my $server ( "server1", "server2", "server3", "server4", "server5" ) {
  $input_q -> enqueue ( $server );
}

$input_q -> end(); #will cause threads to 'bail out' because that while loop will go 'undef'); 

 #wait for threads to complete. 
foreach my $thr ( threads -> list() ) {
   $thr -> join();
}


print "Fail:\n", join ("\n", $failure_q -> dequeue() ), "\n";
print "Success:\n"; join ( "\n", $success_q -> dequeue() ), "\n";

The key points being that your threads - are basically subroutines - and can pass things back and forth using the Queues. end the queue is a good way to handle telling a thread to terminate - there are other ways of course.

Upvotes: 0

Related Questions