まだちゃんと使ってないから問題点とかわからないけど、とりあえずStarmanを使うとかで結構スケールできる気がする。そのあたりを自由に変えられるのもPlack++だね。
使用想定としては、これをplackupしておいて、クライアント AnyEvent::HTTPで http_postして非同期で応答待ちする感じ。
package Lyra::Server::Worker; use Moose; use Router::Simple; use JSON::XS; use namespace::autoclean; use constant NO_SUCH_WORKER => [ 404, [ "Content-Type" => "application/json" ], [ q|{ "status" => 0, "message" => "no such worker" }| ] ]; use constant BAD_PAYLOAD => [ 500, [ "Content-Type" => "application/json" ], [ q|{ "status" => 0, "message" => "bad payload" }| ] ]; use constant WORKER_ERROR => [ 500, [ "Content-Type" => "application/json" ], [ q|{ "status" => 0, "message" => "worker error" }| ] ]; use constant WORKER_SUCCESS => [ 200, [ "Content-Type" => "application/json" ], [ q|{ "status" => 1 }| ] ]; has router => ( is => 'ro', isa => 'Router::Simple', default => sub { Router::Simple->new() } ); sub register { my ($self, $path, $worker, $method) = @_; $method ||= 'process'; $self->router->connect( $path, { controller => $worker, action => $method } ); } sub psgi_app { my $self = shift; return sub { $self->process(@_); } } sub process { my ($self, $env) = @_; my $matched = $self->router->match( $env ); if (! $matched) { return NO_SUCH_WORKER; } my $payload; eval { $payload = $self->fetch_payload( $env ); }; if ($@) { warn $@; return BAD_PAYLOAD; } my $response; eval { my $worker = $matched->{controller}; my $method = $matched->{action}; $response = $worker->$method( $payload ); }; if ($@) { warn $@; return WORKER_ERROR; } if (! $response ) { $response = WORKER_SUCCESS; } return $response; } sub fetch_payload { my ($self, $env) = @_; if ($env->{REQUEST_METHOD} ne 'POST') { return; } my $cl = $env->{ CONTENT_LENGTH }; my $ct = $env->{ CONTENT_TYPE }; # $ct should be application/json, but we're not checking this right now my $input = $env->{ 'psgi.input' }; # Just in case if input is read by middleware/apps beforehand $input->seek(0, 0); my $buffer = ''; my $spin = 0; while ($cl > 0) { $input->read(my $chunk, $cl < 8192 ? $cl : 8192); my $read = length $chunk; $cl -= $read; $buffer .= $chunk; if ($read == 0 && $spin++ > 2000) { Carp::croak "Bad Content-Length: maybe client disconnect? ($cl bytes remaining)"; } } decode_json $buffer; } __PACKAGE__->meta->make_immutable(); 1;
コメント