我正在使用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
有谁知道程序崩溃的原因?欢迎任何帮助,谢谢。
如果我用以下代码替换setsockopt_string
部分,Python脚本运行良好。奇怪的是......我需要深入研究setsockopt_string
函数。
Python 中的新代码:
socket.setsockopt_string( zmq.SUBSCRIBE, "" )
我运行了@ user3666197提供的脚本并获得了调试日志。我只选择日志的几个部分,因为它很长。
答案 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)如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个主题过滤元素到位。如果崩溃,你的关于容量相关限制的假设是错误的。
继续散步!