Tatsumaki
non-blocking web framework built on Plack/AnyEvent

                Tatsuhiko Miyagawa
    Nov. 30th, 2009 / Shibuya.pm Tech Talks #12
PSGI
Perl Web Server
Gateway Interface
Python’s WSGI
 Ruby’s Rack
my $app = sub {
   my $env = shift;
   return [ $status, $headers, $body ];
};
Plain Old Perl
Servers
         CGI, FastCGI, mod_perl1&2
  Standalone, Prefork, AnyEvent, Coro, POE,
Danga::Socket, nginx, mod_psgi, evhttpd, Perlbal
Frameworks
Catalyst, Mason, Maypole, CGI::Application, Jifty
 Dancer, HTTP::Engine, Sqauatting, Continuity
  WebGUI, Movable Type, Ark, Angelos, Noe
Middleware
Static, ConditionalGET, AccessLog, Chunked, Deflater,
    NYTProf, FCGIDispatcher, JSONP, StackTrace,
      Auth::Basic, XSendfile, Rewrite, JSConcat ...
Servers
         CGI, FastCGI, mod_perl1&2
  Standalone, Prefork, AnyEvent, Coro, POE,
Danga::Socket, nginx, mod_psgi, evhttpd, Perlbal
Non-blocking?
psgi.streaming
# delayed response
my $app = sub {
  my $env = shift;
  return sub {
   my $respond = shift;
   $respond->([ $status, $headers, $body ]);
};
# streaming content
my $app = sub {
  my $env = shift;
  return sub {
   my $respond = shift;
   my $w = $respond->([ $status, $headers ]);
   some_event_loop(sub {
     $w->write($content);
     $w->close;
   });
};
WARNING
         psgi.streaming is an interface
Servers may not work if event loop is not shared
  (Recommended to use AnyEvent in your app)
Frameworks?
Most frameworks do not
support non-blocking interface.
     (Mojo 0.99 has IOLoop and Client)
Let’s make one!
Tatsumaki

    http://www.flickr.com/photos/88699006@N00/679056142/
Port of Tornado
Plack and AnyEvent
   All the way from the bottom to the top
Handle thousands of connections with EV/epoll
Works with
AnyEvent, POE, Coro, Danga::Socket and perlbal
            Because it’s AnyEvent!
You can use
Any of AnyEvent::* libraries to do
asynchronous tasks (unlike Mojo)
The first and only framework
  that uses psgi.streaming
package MainHandler;
use base qw(Tatsumaki::Handler);

sub get {
  my $self = shift;
  $self->write(“Hello World”);
}

package main;
use Tatsumaki::Application;
my $app = Tatsumaki::Application->new([
   ‘/’ => ‘MainHandler’,
]);
Tatsumaki::HTTPClient
        AnyEvent::HTTP wrapper
  non-blocking HTTP client for Tatsumaki
package HTTPClientHandler;
use base qw(Tatsumaki::Handler);
__PACKAGE__->asynchronous(1);

use Tatsumaki::HTTPClient;

sub get {
  my $self = shift;
  my $client = Tatsumaki::HTTPClient->new;
  $client->get($api_url, sub {
      my $res = shift;
      my $body = $res->content;
      $self->write(...);
      $self->finish;
  });
}
Long-poll (comet)
package AsyncHandler;
use base qw(Tatsumaki::Handler);
__PACKAGE__->asynchronous(1);

sub get {
  my $self = shift;
  my $t; $t = AE::timer 3, 0, sub {
      undef $t;
      $self->write(“Hello World”);
      $self->finish;
  });
}

package main;
use Tatsumaki::Application;
my $app = Tatsumaki::Application->new([
   ‘/’ => ‘AsyncHandler’,
]);
Tatsumaki::MessageQueue
    Pure perl MQ (in AnyEvent)
package LongPollHandler;
use base qw(Tatsumaki::Handler);
__PACKAGE__->asynchronous(1);

use Tatsumaki::MessageQueue;

sub get {
  my $self = shift;
  my $mq = Tatsumaki::MessageQueue-
>instance(‘ch-name’);
  $mq->poll_once($client_id, sub {
      my @events = @_;
      $self->write(@events);
      $self->finish;
  });
}
package EventPostHandler;
use base qw(Tatsumaki::Handler);

sub post {
  my $self = shift;
  my $mq = Tatsumaki::MessageQueue-
>instance(‘ch-name’);
  $mq->publish({
      name => “NewComment”, ...
  });
  $self->write({ success => 1 });
}
Multipart XHR
JavaScript hack to emulate server push
package MXHRPollHandler;
use base qw(Tatsumaki::Handler);
__PACKAGE__->asynchronous(1);

use Tatsumaki::MessageQueue;

sub get {
  my $self = shift;
  $self->multipart_xhr_push(1);
  my $mq = Tatsumaki::MessageQueue-
>instance(‘ch-name’);
  $mq->poll($client_id, sub {
      my @events = @_;
      $self->stream_write(@events);
  });
}
JS Client libraries
       jquery.ev
      DUI.Stream
       raphaeljs
DEMO
http://192.168.100.50:5000/chat/demo
Other apps
            github.com/gugod/Social
        github.com/miyagawa/Subfeedr
         github.com/audreyt/socialcalc
github.com/clkao/Finance-GeniusTrader-Chart
          github.com/lestrrat/Hamaki
         github.com/yusukebe/Nagare
WHY
Web server resource
I/O bound
!CPU bound
I/O bound web apps
  HTTP API proxy (e.g. OpenSocial)
     Mash up (XML, REST API)
     Real-time Web (Comet)
CPU bound jobs
       Database Servers
 Job Workers (TheSchwartz etc.)
Tatsumaki for
I/O bound web
✓ Non-blocking HTTP client
 ✓ Pure perl Message Queue
 ✓ I/O libraries (AnyEvent::*)
✓ DB libraries (AnyEvent::DBI)
✓ Job dispatcher (AE::Gearman)
Status
 0.1.x on CPAN, considered beta
AnyEvent server has bugs on Linux
Plans
Tatsumaki::Service
Inspired by Google AppEngine Email/XMPP service
               Webhook pattern
XMPP/IRC
Write Jabber/IRC bot as a web application
# see Tatsumaki-Service-XMPP/eg/translate.psgi
package XMPPTranslateHandler;
use base qw(Tatsumaki::Handler::XMPP);

sub post {
  my $self = shift;
  my $msg = $self->xmpp_message;

    my $uri = “http://ajax.googleapis.com/...”;
    my $client = Tatsumaki::HTTPClient->new;
    $client->get($uri, $self->async_cb(sub {
      my $res = shift;
      my $r = JSON::decode_json($res->content);
      $msg->reply($r->{translatedText});
      $self->finish;
    }));
}
Standard Comet Interface
        a.k.a Stardust
Bayeux
HTTP push relay
“Real” PubSub interface
  for Tatsumaki::MQ
Pluggable MQ
using “real” MQ like ActiveMQ or RabbitMQ
DataStore interface
   using AnyEvent::DBI, Redis etc.
See Subfeedr
for Redis integration
  http://github.com/miyagawa/Subfeedr
cpan> install Tatsumaki
http://github.com/miyagawa/Tatsumaki
         irc://irc.perl.org/#plack
That’s it!
Questions?

Tatsumaki