Skip to content
Adam Hopkins edited this page Nov 15, 2018 · 2 revisions

If your app is going to block (see: Blocking/Non-blocking 101), give it more processes and make sure that each process handles only one concurrent connection (-w LARGE* -c 1). With multiple processes, when someone connects to a route and it blocks, the managing process of your app has additional unused processes to send subsequent requests for other routes to and then those requests won't block while your connect route is in the process of blocking.

  • Where LARGE = some sufficiently large number of worker processes to fork. Each HTTP request that is blocked by some code in the Mojo app will block that entire process from doing anything else. If this is the way the process must behave then you must have enough processes to handle as many concurrent connections as you are expecting to handle. Note, this does not scale well; strongly consider re-writing your app if possible to use code which does not block. Sometimes, you may not be able to due to the underlying modules which are in use. At that point, feel free to consider re-writing those modules to support not blocking; or, accept the path that you have chosen and increase the number of workers and perhaps increase the number of physical or virtual servers and use a load balancer. :D

Consider this code:

use Mojolicious::Lite;
 
use Socket;
 
get '/' => {text => 'Hello, World!'};
get '/connect' => sub {
  my $self = shift;
  my ($ip, $protocol, $port, $myhouse, $yourhouse, $log);
 
  $ip = '192.168.0.52';
  $port = '6335'; 
  $protocol = getprotobyname('tcp');
 
  socket(SOCKET, PF_INET, SOCK_STREAM, $protocol);
  $yourhouse = inet_aton($ip);
  $myhouse = sockaddr_in($port, $yourhouse);
 
  $self->app->log->info("Connecting to $ip:$port");
  if (!connect(SOCKET, $myhouse)) {
    $self->render(text => 'KO');
  } else {
    $self->render(text => 'OK');
    close SOCKET || die "close: $!";
      }
};
 
app->start;

And then run it like this: $ perl /tmp/socket.pl prefork -w 10 -c 1

And then test it like this: $ ab -n 3 -c 3 http://127.0.0.1:3000/connect | grep "Time taken for tests"

And you'll see that the total time for ab to complete the 3 requests was 3 seconds, even though each request took itself 3 seconds -- that's because there were 10 processes available to handle the 3 connections, leaving plenty more processes for you to hit up the other non-blocking routes.

Conversely, run it like this: $ perl /tmp/socket.pl daemon

And then test it like this: $ ab -n 3 -c 3 http://127.0.0.1:3000/connect | grep "Time taken for tests"

And you'll see that the total time for ab to complete the 3 requests was 9 seconds because there was only one process available to handle each of the blocking requests. The first one takes 3 seconds, then the next one takes 3 seconds, and so on. 3 requests * 3 seconds each = 9 total seconds.

Or, better of course, is to make your app not block.