我稍微修改了ZeroMQ指南中的helloworld服务器程序(hwserver.pl),以便用AnyEvent实现它。然而,在REQ / REP的两次迭代之后,程序挂起了。有人能找出原因吗?
这是服务器:
#!/usr/bin/perl -w
use strict;
use warnings;
use 5.12.0;
use EV;
use AnyEvent;
use ZMQ::LibZMQ3;
use ZMQ::Constants qw/ ZMQ_REP ZMQ_FD /;
my $context = zmq_init();
my $responder = zmq_socket($context, ZMQ_REP);
my $fh = zmq_getsockopt( $responder, ZMQ_FD );
zmq_bind($responder, 'tcp://*:5555');
our $w; $w = AE::io $fh, 0, sub {
say "Receiving...";
zmq_recv($responder, my $buf, 1_000_000);
say "Received request: [$buf]";
sleep(1);
zmq_msg_send('World', $responder);
sleep(1);
};
EV::run;
这是客户:
#!/usr/bin/perl -w
use strict;
use warnings;
use 5.12.0;
use ZMQ::LibZMQ3;
use ZMQ::Constants qw(ZMQ_REQ);
my $context = zmq_init();
# Socket to talk to server
say 'Connecting to hello world server...';
my $requester = zmq_socket($context, ZMQ_REQ);
zmq_connect($requester, 'tcp://localhost:5555');
for my $request_nbr (0..9) {
say "Sending request $request_nbr...";
zmq_msg_send('Hello', $requester);
my $msg = zmq_msg_init();
say "Receiving...";
zmq_msg_recv($msg, $requester);
say "Received reply $request_nbr: [". zmq_msg_data($msg) ."]";
}
这是服务器的输出:
Receiving...
Received request: [Hello]
Receiving...
Received request: [Hello]
这是客户的输出:
Connecting to hello world server...
Sending request 0...
Receiving...
Received reply 0: [World]
Sending request 1...
Receiving...
Received reply 1: [World]
Sending request 2...
Receiving...
怎么了?
答案 0 :(得分:3)
您是否有理由不使用文档为您的服务器建议的while()语法?
http://search.cpan.org/~dmaki/ZMQ-LibZMQ3-1.00_04/lib/ZMQ/LibZMQ3.pm
从zeromq2-2.1.0开始,您可以使用getsockopt来检索底层证券 文件描述符,所以用它来集成ZMQ :: LibZMQ3和AnyEvent:
my $socket = zmq_socket( $ctxt, ZMQ_REP );
my $fh = zmq_getsockopt( $socket, ZMQ_FD );
my $w; $w = AE::io $fh, 0, sub {
while ( my $msg = zmq_recv( $socket, ZMQ_RCVMORE ) ) {
# do something with $msg;
}
undef $w;
};
此外,睡眠将会阻塞。你应该真正注册一个与定时器事件集绑定的回调,该响应事件将在发送响应的第二个之后运行。
答案 1 :(得分:3)
免责声明:我之前没有真正使用ZeroMQ
首先要注意两件事:
我很确定您遇到了http://funcptr.net/2012/09/10/zeromq---edge-triggered-notification/中描述的问题。 简而言之:
即使zmq文件句柄被通知准备好阅读,不意味着还有等待接收的消息。
回调应该检查这是不是“误报”。
unless (ZMQ_POLLIN & zmq_getsockopt( $responder, ZMQ_EVENTS ) ) {
say "Nothing to recv from socket, skipping";
return;
}
在回调开始时似乎可以做到这一点。
但我认为这不重要。这个问题只是让你在数据存在之前调用zmq_recv,所以它会使你的程序更加阻塞 即使收到多条消息,也只能调用一次IO观察程序。在上面“ZeroMQ使用边缘触发”段落中链接的文章中对此进行了很好的描述。这就是为什么你应该在回调内循环,直到没有更多的消息。您可以通过以下方式以非阻塞方式执行此操作:
while (my $msg = zmq_recvmsg($responder,ZMQ_DONTWAIT)) {
say "Received request: [".zmq_msg_data($msg)."]";
zmq_msg_send('World', $responder);
}
此循环将处理所有待处理的邮件,如果没有则会离开。
UPDATE 链接文章建议在循环中接收消息,而获取ZMQ_EVENTS的getsockopt报告ZMQ_POLLIN已设置。这对我来说似乎更优雅。我没有测试它,也不知道是否存在实际差异。
我不知道你的场景中如何同时服务器可能会收到多条消息,但单独上面的代码更改似乎可以解决问题。可能我不太了解ZeroMQ。如果有人能够向我解释这个具体案例中的“为什么”,我会很高兴。