我很担心poller实际上在zmq中做了什么。 zguide最低限度地进入它,并且仅将其描述为从多个套接字读取的方式。这对我来说不是一个令人满意的答案,因为它没有解释如何使用超时套接字。我知道zeromq: how to prevent infinite wait?解释了推/拉,但不是req / rep模式,这是我想知道如何使用的。
我想要问的是:poller如何工作,它的功能如何应用于跟踪套接字及其请求?
答案 0 :(得分:57)
当您需要侦听同一线程中的不同套接字时,请使用轮询器:
ZMQ.Socket subscriber = ctx.socket(ZMQ.SUB)
ZMQ.Socket puller = ctx.socket(ZMQ.PULL)
使用轮询器注册套接字(POLLIN
侦听传入的消息)
ZMQ.Poller poller = ZMQ.Poller(2)
poller.register(subscriber, ZMQ.Poller.POLLIN)
poller.register(puller, ZMQ.Poller.POLLIN)
轮询时,请使用循环:
while( notInterrupted()){
poller.poll()
//subscriber registered at index '0'
if( poller.pollin(0))
subscriber.recv(ZMQ.DONTWAIT)
//puller registered at index '1'
if( poller.pollin(1))
puller.recv( ZMQ.DONTWAIT)
}
选择您要如何投票...
poller.poll()
阻塞,直到任一套接字都有数据
poller.poll(1000)
阻止1秒,然后超时。
轮询器通知套接字上何时有数据(消息);阅读它是你的工作。
阅读时,请不要阻止:socket.recv( ZMQ.DONTWAIT)
。即使poller.pollin(0)
检查是否有要读取的数据,您仍希望避免在轮询循环内进行任何阻塞调用,否则,由于“卡住”套接字,您最终可能会阻塞轮询器。
因此,如果向subscriber
发送两条单独的消息,则必须调用subscriber.recv()
两次以清除轮询器,否则,如果您调用subscriber.recv()
一次,则轮询器将继续告诉你还有另一条消息需要阅读。因此,从本质上讲,轮询器会跟踪消息的可用性和数量,而不是实际消息。
您应该浏览一下轮询示例并使用代码,这是最好的学习方式。
这会回答你的问题吗?
答案 1 :(得分:3)
在这个答案中我列出了
文档中的详细信息 http://api.zeromq.org/4-1:zmq-poll
此外,我还添加了一些重要的解释和为新用户清除混乱的内容!如果你赶时间!您可能希望从轮询器做什么以及接收怎么样和关于接收的注释以及仅一个套接字怎么样部分开始!从重要笔记部分开始!我在那里深入清除事物!我仍然建议仔细阅读文档参考中的细节!还有第一部分!
zmq_poll() 函数为应用程序提供了一种机制,可以在一组套接字上以电平触发的方式多路复用输入/输出事件。 items 参数指向的数组的每个成员都是一个 zmq_pollitem_t 结构。 nitems 参数指定 items 数组中的项目数。 zmq_pollitem_t 结构体定义如下:
typedef struct
{
void //*socket//;
int //fd//;
short //events//;
short //revents//;
} zmq_pollitem_t;
对于每个 zmq_pollitem_t 项,zmq_poll() 应检查ØMQ 套接字 套接字引用的 或指定的标准套接字文件描述符 fd,用于事件中指定的事件。 如果在一个zmq_pollitem_t中同时设置了socket和fd,则socket引用的ØMQ socket优先,忽略fd的值。
重要说明(相同的上下文):
<块引用>传递给 zmq_poll() 函数的所有 ØMQ 套接字必须共享相同的 ØMQ 上下文,并且必须属于调用 zmq_poll() 的线程。
对于每个 zmq_pollitem_t 项,zmq_poll() 将首先清除 revents 成员,然后通过设置与 revents 成员中的事件条件相对应的位来指示已发生的任何请求事件。
<块引用>成功完成后,zmq_poll() 函数应返回 zmq_pollitem_t 结构的数量,其中事件在 revents 中发出信号,如果没有事件发出信号,则返回 0。
如果没有请求的事件发生在任何zmq_pollitem_t项目 , zmq_poll() 应等待超时微秒,以便在任何请求的项目上发生事件。如果timeout的值为0,zmq_poll()会立即返回。如果timeout 的值为-1,zmq_poll() 将无限期地阻塞 直到在至少一个 zmq_pollitem_t 上发生了请求的事件。 超时分辨率为1毫秒。
0 => 不等待
-1 => 阻止
+val => 阻塞并等待超时时间
zmq_pollitem_t 的 events 和 revents 成员是位掩码,通过 OR'ing 以下事件标志的组合构造:
对于ØMQ套接字,至少可以从套接字接收一条消息而不会阻塞。对于标准套接字,这相当于 poll() 系统调用的 POLLIN 标志,通常意味着可以从 fd 读取至少一个字节的数据而不会阻塞。
对于ØMQ套接字,至少可以向套接字发送一条消息而不会阻塞。对于标准套接字,这相当于 poll() 系统调用的 POLLOUT 标志,通常意味着至少可以将一个字节的数据写入 fd 而不阻塞。
对于标准套接字,此标志通过 zmq_poll() 传递到底层 poll() 系统调用,通常意味着 fd 指定的套接字上存在某种错误条件。对于 ØMQ 套接字,如果在事件中设置此标志无效,并且永远不会在 zmq_poll() 中返回。
注意:
<块引用>zmq_poll() 函数可以使用除 poll() 之外的操作系统接口来实现或模拟,因此可能会以本文档中未定义的方式受到这些接口的限制。
成功完成后,zmq_poll() 函数应返回 zmq_pollitem_t 结构的数量,其中事件在 revents 中发出信号,如果没有事件发出信号,则返回 0。失败时,zmq_poll() 应返回 -1 并将 errno 设置为下面定义的值之一。
无限期轮询 0mq 套接字和标准套接字上的输入事件。
zmq_pollitem_t items [2];
/* First item refers to ØMQ socket 'socket' */
items[0].socket = socket;
items[0].events = ZMQ_POLLIN;
/* Second item refers to standard socket 'fd' */
items[1].socket = NULL;
items[1].fd = fd;
items[1].events = ZMQ_POLLIN;
/* Poll for events indefinitely */
int rc = zmq_poll (items, 2, -1);
assert (rc >= 0); /* Returned events will be stored in items[].revents */
轮询器只检查并等待事件发生时!
POLLIN 用于接收!数据在那里接收!
那么我们应该通读recv()!我们有责任阅读或做任何事情!轮询器只是在那里聆听事件并等待它们!通过 zmq_pollitem_t
我们可以监听多个事件!万一发生什么事情!然后轮询器解锁!我们可以在 recv 中检查事件!和 zmq_pollitem_t!请注意,轮询器在事件触发时将事件排队!下一个电话将从队列中挑选!因为那个顺序也被保留了!连续调用将返回下一个事件,依此类推!当他们进来时!
对于路由器!一台路由器甚至可以接收来自一个客户端的多个请求!并且同时来自多个客户!在多个客户端具有相同性质的设置中!并且是连接到路由器的那些!一个新人可能会想到的问题是!对于这种异步性质,我是否需要轮询器!答案是否定的!不需要轮询器和监听不同的套接字!
重要的一点是:接收调用(zmq_recv()、socket.recv() 一些 lang 绑定)!堵塞!并且是阅读的方式!当消息来了!他们在排队!轮询者与此无关!轮询器只监听来自不同套接字的事件!并在其中任何一个发生时解除阻止!如果达到超时,则不会发生任何事件!仅此而已!
接收的本质是直截了当的!接收呼叫阻塞!直到消息队列中的消息来了!当多人来时,他们会排队!然后在每次下一次调用 recv() 时!我们会拉下一个消息!或框架! (取决于我们使用的接收方法!和api级别!以及从绑定库到低级别的抽象!) 因为我们也可以按帧访问消息!每次调用一帧! 但是到这里就清楚了! 接到电话是要接到的东西!他们阻塞直到消息进入队列!多条并行消息!他们来的时候会进入队列!然后每次调用!队列是否已满!消耗它!或等待! 这是一件非常重要的事情!哪些会迷惑新用户!
只有在有多个套接字时才需要轮询器!它们始终是我们在相关进程代码上声明的套接字(绑定它们,或连接到某些东西)!因为如果没有!您将如何收到消息!你不能做得很好!因为你必须优先考虑其中一个!在有一个 recv() 的循环中先行!哪个会堵!即使另一个套接字在它的队列中收到一条消息!循环被阻塞,无法进行下一个recv()!因为轮询器给了我们能够解决这个问题的美!并且可以很好地与多个套接字配合使用!
while(true) {
socket1.recv() // this will block
socket2.recv() // this will have to wait till the first recieve! Even if messages come in in it's queue
使用轮询器:
While(true) {
zmq_poll() // block till one of the socket events happen! If the event was POLLIN!
// If any socket get a message to it's queue
// This will unblock
// then we check which type and which socket was
if (condition socket 1) {
// treat socket 1 request
}
if (condition socket 2) {
// treat socket 2 request
}
// ...
}
您可以在 this section 处查看文档中的真实代码(滚动到足以查看代码块,您也可以在所有不同的语言中查看)
知道轮询器只是通知有消息进来!如果是 POLLIN!
在每次迭代中!如果许多事件已经触发,则轮询器! Let's give the example of 10 messages recieved 5 in each socket
!此处轮询器已经将事件排入队列!并且在每次下一次调用 9 次!会立即解决!有问题的消息可以映射到什么套接字(通过使用轮询对象和平均值!因此绑定库使它变得过于简单和令人愉快)!然后正确的套接字块将进行接收调用!当它这样做时它会从它的队列中消耗它的下一条消息!
所以你不断循环并且每次都消耗下一条消息! 他们进来时!并且轮询器跟踪了进来的顺序! 通过订阅和选择收听的事件!在接收的情况下,应该是POLLIN!
然后每个套接字都有它的消息队列!并且每次接到电话! 从中拉出! 轮询器跟踪了他们!所以当轮询器解析时! 保证socket接收调用有消息!
最后一个例子:服务器客户端模式
让我们以一台服务器(路由器)和许多连接到的客户端(经销商)为例!如下图所示!
问题:许多连接到同一路由器!异步来一次!还有啦啦啦啦啦!在服务器端(路由器)! 我需要轮询器吗!?很多新人可能会认为是的,或者质疑是否需要!是的,你猜对了!
大错!
为什么? 因为在服务器(路由器)代码中!我们只有一个套接字要处理!我们绑定!然后客户端连接到它!为此!只有一个插座!并且所有 recv() 调用都在那个套接字上!那个套接字有它的消息队列! recv() 一个接一个消费消息!异步和它们如何来无关紧要!再次轮询器仅在有多个套接字时!因此具有处理来自多个套接字的消息的混合性质!如果不!然后一个套接字的一个 recv() 需要先去另一个!并且会挡住对方!不是什么好事(坏事)!
这个答案带来了一个很好的清理!此外,它还引用了具有良好突出显示的文档!还通过低级库(c lang)显示代码! @rafflan 的 answer 显示了一个带有绑定库的很棒的代码(似乎是 c#)!和一个很好的解释!如果你没有检查它,你必须!