我是Plack,Twiggy和AnyEvent的新手,遇到了问题。

我有面向客户端的应用程序服务器,可将请求发送到后端游戏服务器。游戏服务器可以做几件事。
1.当来自应用程序服务器的请求到达时,它们对对象执行操作(例如,移动,购买,删除或启动对象)。
2.他们会定期更新所有“可移动”对象的位置(更新经纬度)。

为了实现这一目标,我设置了一个非常基本的psgi文件,该文件执行以下操作:
AnyEvent用于每两秒(重复一次)更新对象的经度/纬度。

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做到这一点,还是我走错了方向?
如果这是正确的,我该如何解决?
我还有其他方法可以更好地解决这个问题吗?

就像我说的那样,我对此很陌生,如果没有提供必要的信息,对不起。请务必提出要求,我将提供所有错过的信息。

谢谢。

最佳答案

我想指出的是,最好将某些“后台”处理与处理HTTP请求分开。最好是,HTTP服务器/应用程序不必担心每两秒钟运行一次操作。例如,这可能在单独的进程中发生,但需要将游戏状态保持在某种类型的数据库中(而不是仅在内存中具有哈希值-正如我现在所了解的代码)。

考虑到这一点,让我们开始处理您的特定问题。
您的应用之前不能包含$w->recv,因为这会阻止。如果仅删除它,则您的AnyEvent观察程序将在应用运行时立即超出范围,并且不会触发任何计时器事件。解决方案是根本不调用$w->recv(不需要阻止-事件循环将始终由Twiggy调用),而只是确保确保有人在引用$w以防止其被垃圾回收。

当您运行psgi文件时,唯一可以保留的内容是对应用程序的引用。如果该应用程序关闭了您的$w,则仍将对其进行引用,并且计时器将起作用。

最简单的解决方案是让$w出现在$app中。就像这样:

$app = sub {
    my $env = shift;
    $w;
    ... rest of app code ...
};

关于perl - twiggy plack anyevent在主循环阻塞时如何处理请求?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23610969/

10-12 19:47