我对Plack,Twiggy和AnyEvent都很陌生并遇到问题。
我的客户端面向应用程序服务器,它们向后端游戏服务器发送请求。游戏服务器做了两件事
1.当请求从应用程序服务器到达时,它们对对象执行操作(例如,移动,购买,删除或发送对象)。
他们定期更新所有可移动的位置。对象(更新lat / lng)。
为了尝试实现这一点,我设置了一个非常基本的psgi文件,该文件执行以下操作: AnyEvent用于每两秒(重复)更新对象的lat / lng。
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只处理从应用程序服务器启动的请求以对对象执行某些操作。为完整起见,App服务器上的代码如下;
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;
}
在游戏服务器端,代码看起来像这样
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 ],
];
}
零件单独工作。我的意思是,如果我只是担心获取和处理RPC请求到游戏服务器它一切正常。
如果我只是使用AnyEvent定期更新工作正常的对象位置。
如果我想在同一组对象上同时执行这两个操作,则会出现问题。
它失败了,因为$ cond-> recv阻止我们并没有实际到达$ app所以Twiggy没有处理请求或者如果我移动$ cond行,我们启动应用程序和twiggy等待来自应用服务器的请求,但AnyEvent事件不会发生(或者它们会发生一次)。
Twiggy应该是非阻塞的(我认为在这里很有用),并且考虑到AnyEvent构建,所以我假设有办法做我想要的,但是看了一会儿,我没有走得太远。
有没有办法使用Twiggy,Plack和AnyEvent这样做,还是我朝错误的方向前进? 如果这有点对,我该如何解决呢? 还有另外一种方法可以更好地解决这个问题吗?
正如我所说,如果我没有提供重要信息,我很陌生。请问,我会用我错过的任何内容进行更新。
感谢。
答案 0 :(得分:1)
我想指出,最好将一些“后台”处理与处理HTTP请求分开。如果HTTP服务器/应用程序不必担心每两秒运行一次操作,那将是最好的。例如,这可能发生在单独的进程中,但需要将游戏状态保存在某种类型的DB中(而不是仅仅在内存中使用哈希 - 因为我现在理解您的代码)。
考虑到这一点,让我们转到您的具体问题。
您的应用程序之前不能有$w->recv
,因为这会阻止。如果您只是删除它,那么当应用程序运行时,您的AnyEvent观察程序将立即超出范围,并且不会触发计时器事件。解决方法是根本不调用$w->recv
(不需要阻塞 - 无论如何都会被Twiggy调用事件循环),但只是确保某些东西引用$w
以防止它被垃圾收集
只有在运行psgi文件时仍然存在的东西才是对应用程序的引用。如果该应用程序关闭了$w
,那么它仍将被引用,并且计时器将起作用。
最简单的解决方案是在$w
内显示$app
。就像那样:
$app = sub {
my $env = shift;
$w;
... rest of app code ...
};