zmq_send()在recv/poll处阻塞:inproc pair套接字发送小消息异常问询
ZMQ inproc pair套接字发送阻塞问题解答
异常触发原因
给出的堆栈说明zmq_send已经进入阻塞等待状态,核心原因是发送端套接字的队列已满,且调用zmq_send时未设置ZMQ_DONTWAIT标志,此时发送操作会挂起直到有可用的发送缓冲。
libc.so.6!__GI___poll(struct pollfd * fds, nfds_t nfds, int timeout) libzmq.so.5!poll(int __timeout, nfds_t __nfds, pollfd * __fds) libzmq.so.5!zmq::signaler_t::wait(zmq::signaler_t * const this, int timeout_) libzmq.so.5!zmq::mailbox_t::recv(zmq::mailbox_t * const this, zmq::command_t * cmd_, int timeout_) libzmq.so.5!zmq::socket_base_t::process_commands(zmq::socket_base_t * const this, int timeout_, bool throttle_) libzmq.so.5!zmq::socket_base_t::send(zmq::socket_base_t * const this, zmq::msg_t * msg_, int flags_) libzmq.so.5!s_sendmsg(int flags_, zmq_msg_t * msg_, zmq::socket_base_t * s_) libzmq.so.5!zmq_send(void * s_, const void * buf_, size_t len_, int flags_)
ZMQ此时处理的命令类型
这里mailbox_t::recv接收的是ZMQ内部跨线程通信的命令,不是用户层的业务消息。当前阻塞等待的是对端套接字发来的发送缓冲可用通知,其余可能的命令还包括连接状态变更、套接字销毁指令等。
发送流程中调用recv()的原因
ZMQ采用异步事件驱动的IO模型,无论收发用户消息,执行流程中都会优先处理内部命令队列:
- 发送消息时如果检测到发送缓冲已满,会先处理所有待执行的内部命令,确认是否有对端释放缓冲的通知
- 此处的recv是针对ZMQ内部命令通信管道(mailbox)的操作,和用户层调用的
zmq_recv没有关联。
是否由高水位标记(HWM)导致
即使单条消息体积很小,也完全可能触发HWM,同时也存在其他诱因:
- ZMQ的高水位标记统计的是消息条数而非字节数,inproc套接字的默认HWM通常为1000条,只要发送速度远高于对端接收速度,累计条数达到阈值就会触发流控,和单条消息大小无关。
- 其他可能诱因:单线程内同时操作pair两端,发送大量消息后才执行接收操作;对端线程已卡死、退出,不会再接收消息。
高水位触发的测量方法
可以通过以下方式确认是否为HWM导致的阻塞:
- 创建套接字时,通过
zmq_setsockopt将ZMQ_SNDHWM、ZMQ_RCVHWM设置为远大于测试发送条数的值,若阻塞问题消失则可确认是HWM触发 - 调用
zmq_getsockopt获取ZMQ_EVENTS选项,若返回值不包含ZMQ_POLLOUT标志,说明当前发送缓冲已满,已触发流控 - 调用
zmq_send时添加ZMQ_DONTWAIT标志,若接口返回EAGAIN错误,可明确是发送缓冲已满,大概率为HWM导致。
内容的提问来源于stack exchange,提问作者Grzegorz
相关产品推荐
相关产品推荐

