I'm very new to Plack, Twiggy and AnyEvent and having a problem.
I have client facing application servers that fire requests to a backend game server. The game servers do a couple of things.
1. they do things to objects when a request arrives from an application server (for example, move, buy, delete, or launche objects).
2. they regularly update the positions of all 'movable' objects (update lat/lng).
To try and achieve this I've setup a very basic psgi file which does the following: AnyEvent is used to update the lat/lng of the objects every two seconds (repeatedly).
use AnyEvent;
my $cond = AnyEvent->condvar;
my $w; $w = AnyEvent->timer ('after' => 0, 'interval' => 2, cb => sub {
while ( my ($key, $o) = each %{$self->{'objhash'}})
{
$self->position->update($o) if($o->{type} eq 'movable');
}
});
$cond->recv;
my $app = sub {
my $env = shift;
my $resp = $b->handler_rpc($env); #deal with application server requests (e.g. delete object)
return $resp;
};
handler_rpc simply deals requests that are kicked off from the application servers to do something to an object. For completeness the code on the App servers is as follows;
sub request_action
{
my $self = shift;
my $vals = shift;
my $y = $self->y;
use AnyEvent;
use AnyEvent::RPC;
use Data::SplitSerializer;
use Data::Dump qw(pp);
my $flattened = Data::SplitSerializer->new( path_style => 'DZIL' )->serialize($vals);
pp $flattened;
my $rpc = AnyEvent::RPC->new(
host => '192.168.56.20',
port => 5000,
base => '/',
type => 'REST', # or type => '+AnyEvent::RPC::Enc::REST',
#debug => 10,
timeout => 5,
);
my $cv = AE::cv;
$cv->begin;
my $response;
$rpc->req(
method => 'POST',
call => [ method => qw(handler_rpc)], #Not used atm
query => $flattened,
cb => sub { # ( response, code, error )
if ($response = shift) {
warn "we had this response " . Dumper($response);
$response = $response->{response};
$cv->end;
} else {
my ($code,$err) = @_;
$response = { 'status' => 'UNKNOWN_ERROR',
'code' => $code,
'error' => $err };
warn "we have an error $code, $err";
}
},
);
$cv->recv;
return $response;
}
And on the game server side the code looks like this
sub handler_rpc {
my $self = shift;
my $vals = shift;
use Plack::Request;
my $req = Plack::Request->new($vals);
use Data::SplitSerializer;
use Data::Dump qw(pp);
use URI;
use URI::QueryParam;
my $params = $req->parameters;
my $deep = Data::SplitSerializer->new( path_style => 'DZIL' )->deserialize($req->parameters);
pp $deep;
#process the command
my $resp;
if( my $func = $self->can($deep->{cmd}) )
{
warn "Calling method of " . $deep->{cmd};
$resp = &$func( $self, $deep->{args} );
}
my $xs = new XML::Simple;
my $xml = $xs->XMLout($resp,
NoAttr => 1,
RootName=>'response', #TODO: put some key in here somewhere for security.
);
return [
200,
['Content-Type' => 'text/plain'],
[ $xml ],
];
}
The parts work individually. By that I mean, if I just worry about getting and processing RPC requests to the game server it all works fine.
If I just use AnyEvent to regularly update the object locations that works fine.
The problem occurs when I want to do both at the same time on the same set of objects.
It fails because either the $cond->recv blocks and we don't actually get to the $app so Twiggy doesn't process requests or if I move the $cond line, we start the app and twiggy waits for requests from the app servers but the AnyEvent events don't occur (or they occur once).
Twiggy is supposed to be non-blocking (which I think is useful here) and built with AnyEvent in mind, so I'm assuming there is a way to do what I want, but having looked around for a while, I'm not getting far.
Is there a way to do this using Twiggy, Plack and AnyEvent or am I heading the wrong direction?
If this is somewhat right how do I fix it?
Is there another way I could approach this altogether that would be better?
As I said, I'm quite new to this so sorry if I'm not provided essential information. Please do ask and I'll update with anything I've missed.
Thanks.