gpt4 book ai didi

perl - twiggy plack anyevent 如何在主循环阻塞时处理请求?

转载 作者:行者123 更新时间:2023-12-02 03:35:39 27 4
gpt4 key购买 nike

我对 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 只是处理从应用程序服务器启动的请求,以便对对象执行某些操作。为了完整起见,应用服务器上的代码如下;

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/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com