ZeroMQ:SUBs可以订阅的主题数量有限制吗?

时间:2017-05-15 13:49:26

标签: python c zeromq pyzmq

我正在使用ZeroMQ的PUB / SUB套接字模式。 PUB生产股票'财务数据并发布。该主题设置为每个股票的代码。在SUB端,客户可以根据股票代码订阅他们想要的数据。 PUB用C语言编写,SUB用Python编写。

然而,在测试期间出现了问题。如果在SUB套接字上只将一个股票代码设置为消息过滤器,则一切正常。但是当涉及到大量股票时,程序会在短时间内崩溃,从而产生错误" Segmentation fault(core dumped)" (详情如下)。

以下是 PUB(C)

的代码
while (1) {  
    int rc = 0;
    // send topic
    rc = zmq_send(pub_socket, topic, rc, ZMQ_SNDMORE);
    if (rc == -1) {
        // error handling
    }
    // send stock data
    rc = zmq_send(pub_socket, data, rc, 0);
    if (rc == -1) {
        // error handling
    }
}

以下是 SUB(Python)

的代码
import zmq

# initialize a SUB socket
ctx = zmq.Context.instance()
socket = ctx.socket(zmq.SUB)

# set socket options to filter message
for code in code_list:
    socket.setsockopt_string(zmq.SUBSCRIBE, code)

socket.connect(PUB_ADDR)
# recv data from PUB
while True:
    data = socket.recv()
    print(data)

我还使用gdb来调试程序。 调试结果如下: debug result    enter image description here

有谁知道程序崩溃的原因?欢迎任何帮助,谢谢。

更新

如果我用以下代码替换setsockopt_string部分,Python脚本运行良好。奇怪的是......我需要深入研究setsockopt_string函数。

Python 中的新代码:

socket.setsockopt_string( zmq.SUBSCRIBE, "" )

最新更新

我运行了@ user3666197提供的脚本并获得了调试日志。我只选择日志的几个部分,因为它很长。

套接字初始化 Initialization setsockopt_string完成了 setsockopt_string finished recv one msg并退出 recv one msg and exited

1 个答案:

答案 0 :(得分:1)

说明:

PUB -side使用ZeroMQ v 4.1.5; SUB -side使用ZeroMQ Python包装器16.0.2

隐式地,这会使PUB/SUB模式与前几代API相比回到v 2.0,依赖于 PUB - 侧过滤,而你的 SIGSEGV 指示报告SUB - 方面的问题。

尽管上述假设过滤是根本原因,但我还记得一些关于大树过滤问题的技术争论,还有一个小小的惊喜,就像在一些关于Trie-search的帖子中一样,增加了 "" leaf-node也提供了一项神奇的服务。如果可以帮助的话,会再次尝试找到这场辩论。

初始remarks from Martin Sustrik refer up to 在ZeroMQ过滤器中约有10,000个订阅不会产生问题(在进一步的设计讨论中有一些更高的数字):

  

高效订阅匹配

     

在ZeroMQ中,简单的尝试用于存储和匹配PUB/SUB订阅。订阅机制适用于多达10,000个订阅,其中简单的trie运行良好。但是,有些用户使用多达150,000,000个订阅。在这种情况下,需要更有效的数据结构。因此,nanomsg使用内存效率版本的Patricia trie而不是简单的trie。

     

有关详细信息,请查看this article

始终使用分步方法诊断原因:

一次轻微的测试修改将使您更接近打开问题的真实信封:

import zmq
pass;                                           print "DEBUG: Ok, zmq imported. [ver:{0:}]".format( zmq.pyzmq_version() )
#_______________________________________________# SETUP ZMQ:
ctx    = zmq.Context( 2 )                       # Context( nIOthreads )
pass;                                           print "DEBUG: Ok, zmq.Context() instantiated."  
socket = ctx.socket(  zmq.SUB )                 # Socket(  .SUB )
pass;                                           print "DEBUG: Ok, Socket instantiated."
socket.connect(           PUB_ADDR )            # .connect()
pass;                                           print "DEBUG: Ok, .connect() completed."
socket.setsockopt(   zmq.LINGER, 0 )            # explicit LINGER
pass;                                           print "DEBUG: Ok, .setsockopt( LINGER, 0 ) completed."
#_______________________________________________# SET FILTER:
for code in code_list:
    pass;                                       print "DEBUG: Going to set SUB side n-th filter: {0: > 1000d}. == [{1:}]".format( code_list.index( code ), repr( code ) ),
    socket.setsockopt_string( zmq.SUBSCRIBE, code )
    pass;                                       print "DEBUG: Ok, this one was done."
pass;                                           print "DEBUG: Ok, all items from <code_list> exhausted."
#_______________________________________________# LOOP FOREVER:
while True:
    try:
          print "LOOP: .recv() call."
          data = socket.recv()
          print "LOOP: .recv()-ed {0:}[B] repr()-ed as [{1:}]".format( len( data ), repr( data ) )

    except KeyboardInterrupt():
          print "EXC: Ctrl-C will terminate."

    except:
          print "EXC: will terminate."

    finally:
          pass;                                 print "DEBUG: Ok, finally: section entered:"
          socket.close()
          pass;                                 print "DEBUG: Ok, Socket instance .close() call returned"
          ctx.term()
          pass;                                 print "DEBUG: Ok, .Context() instance term()-ed"
          break

鉴于测试案例描述为只有一个 PUB 和一个 SUB ,其他性能扩展&amp;详细的缓冲管理问题目前不会引发问题。在运行moded ed测试并发布简单的DEBUG:log。

之后,将会看到结果

每秒发送大约3k条消息也不是问题。

更新:遗漏点 - (1)Unicode处理+(2)主题过滤器

(1)如DEBUG:log中所示,您可以混合使用Unicode和普通的C字节数组。 These representations MUST match - system-wide(从.send_string()开始,通过.setsockopt_string(),直到.recv_string()

data = socket.recv_string()           # AS YOUR DEBUG:log shows the b'mkt_bar...'

(2)主题 - 过滤器必须匹配 - 否则邮件将被整理为非订阅的邮件...这样就可以了... '过滤器匹配u'abc ....'消息。不是这样的:

  

setsockopt_string( option, optval, encoding='utf-8' )   

  长度为零的空 optval 将订阅所有传入的消息。 非空optval 应订阅以指定前缀开头的所有邮件。多个过滤器可以附加到单个ZMQ_SUB套接字,在这种情况下是一条消息如果匹配至少一个过滤器,则应被接受。

DEBUG:上面提供的日志片段显示(好吧,PrintScreens ...... - 请下次相当复制/粘贴终端ASCII ,而不是图片,除非显示一些GUI功能,对吧?Thx ...),您的主题过滤器在定义的意义上永远不会匹配。修复它。全系统。

ZeroMQ不应该归咎于此,如果混合或者在错误的调用接口中,Unicode + C字节数组根本无法工作和headbang。

尾声:

如果仍然指责ZeroMQ主题过滤能力,最简单的a / b测试(dis) - 批准Null假设将是运行非常相同的测试,但只有5个主题过滤元素到位。如果崩溃,你的关于容量相关限制的假设是错误的。

继续散步!