[英]Is ZeroMQ XPUB recv() a solution for finding if there is a subscriber and solve the slow joiner syndrome?
我的用例:
慢連接問題:
僅運行 1000 多個線程(發布者)1 或 2 次,我就獲得了訂閱者中的所有數據。 添加幾毫秒的睡眠解決了這個問題,所以我有 99.9% 的把握我是眾所周知的慢連接器綜合症的受害者。 然而,就我而言,睡眠解決方案並不是一個好的解決方案,因為發布者的連接時間可能是可變的,我希望盡快將數據發送給訂閱者。
我對解決這個問題的想法和實驗代碼:
我的解決方案基於使用 XPUB recv 方法。 使用 XPUB 初始化發布者並將 RCVTIMEO 設置為 1000 毫秒。 發布者連接后,我添加了一個recv()
調用來檢查是否有訂閱者。 當我收到訂閱消息時,我知道連接已經完成,並且我可以發送數據而不會丟失任何數據(除非訂閱者發生錯誤但我不在乎)。
如果我沒有收到任何訂閱消息,那么在 1000 毫秒recv()
超時並且線程終止。
這是python(pyzmq)中用於測試此實現的示例代碼(對於發布者,我不使用線程,而是使用while循環並同時運行多個發布者),它可以按我的意願工作:
發布者.py:
import zmq
def main():
""" main method """
i = 0
while True:
# Prepare context and publisher
context = zmq.Context()
publisher = context.socket(zmq.XPUB)
publisher.connect("tcp://0.0.0.0:5650")
publisher.setsockopt(zmq.RCVTIMEO, 1000)
# Waiting for 1000ms to get a subscription
i = i + 1
try:
publisher.recv()
# Send the message
publisher.send_multipart([b'test', bytes(str(i),'utf-8')])
except Exception as e:
print(e, flush=True)
# Terminate socket and context
publisher.close()
context.term()
if i >= 10000:
break
if __name__ == "__main__":
main()
訂閱者.py:
import zmq
def main():
""" main method """
# Prepare our context and subscriber
context = zmq.Context()
subscriber = context.socket(zmq.SUB)
uri = "tcp://0.0.0.0:5650"
subscriber.bind(uri)
subscriber.setsockopt(zmq.SUBSCRIBE, b'')
print('Subscriber connects to %s' % (uri), flush=True)
# Receive messages
i = 0
while True:
[topic, data] = subscriber.recv_multipart()
i = i + 1
print("%s: %s %s" % (i, topic, data), flush=True)
if __name__ == "__main__":
main()
我的問題:
解決方案就這么簡單嗎? 如果有訂閱者處於活動狀態,我是否遺漏了任何會導致數據丟失的內容(與慢速加入者有關)?
問: “解決方案就這么簡單嗎?”
恰恰相反。 對於上面發布的內容,解決方案過於復雜 w.r.t。 到目前為止發布的用例需求。
a) 鑒於上述要求,在位於同一主機上的線程之間進行通信時,可以消除與 ISO-OSI-L3 tcp://
傳輸 Class 的設置和維護相關的所有成本過程。 而是 go 用於超快速、無堆棧、內存映射的inproc://
傳輸 Class 以避免所有這些低效率。 ZeroMQ API v4.0+ 在設置inproc://
-TransportClass {.bind() |.connect() }
-出現順序上也沒有其他條件的舒適,因此我們可以享受最大的MEM映射超- 零拷貝“傳輸”消息的低延遲標記(不移動 RAM 中數據的一個字節) - 很酷,不是嗎? (除非您需要注入 MITM 協議嗅探,否則請刪除tcp://
overkill )
b) 考慮到上述要求,傳遞幾個消息,其中“靜態” SUB
方訂閱所有消息,對PUB/SUB
可擴展正式通信模式原型的使用非常低效。 您的代碼必須支付所有費用來設置一個新的SUB
實例,然后它會爬行以設置一個有效的連接(通過tcp://
' 堆棧,希望在 a) 下刪除),接下來要設置一個新的 TOPIC-過濾器(無論是在早期版本中的 SUB 端運行,還是在較新的 ZeroMQ 版本中的 PUB 端運行——所有這些都以接收所有消息為代價——即根本沒有過濾)。 使用更輕量級的多節點( PUSH/PULL
-on-one-node)方式可以實現相同的正式服務。 如果沒有其他需要任何反向/雙向/更復雜的正式通信,這只是一個PUSH/PULL
將能夠完成所要求的工作。
c) 鑒於上述要求,您的重點似乎是過早地通過連接發送消息而不會丟失消息。 在 ZeroMQ 設置中有用於確定這一點的工具,但您不小心使用它們:
zmq.IMMEDIATE
可以使用 AccessNode 的阻塞 state 在沒有現成連接工作的情況下(或曾經)errno
(或zmq.errno()
用於 POSIX 不兼容的操作系統/Win32 等)處理可以幫助您的代碼檢測和對“自治代理網絡”中發生的任何和所有特定情況做出反應“在分布式計算中貫穿其生命周期的整個跨度(無論代理是否實際上是“物理上”分布或位於同一位置,就像這里的情況一樣)。 不失控是這里的核心責任。 什么是控制代碼,它在失去控制的 state 中自鎖,它甚至無法控制自己;)? d) 永遠不要使用{.recv() |.send() |.poll() |... }
方法的阻塞形式。 教科書示例是專業信號/消息傳遞元平面實現應該是什么樣子的反模式。 確實從來沒有 - 參考。 第 5) 項。
e) 更好地重用Context()
-instance,而不是像上面所描繪的那樣使其成為消耗品/一次性用品。 線程可以自由地共享一個預先實例化的Context()
-engine,避免下一個巨大的重復性附加開銷成本,如果為每個分叉重新實例化一個消耗性/一次性Context()
,只是一個短暫的、對等的客戶端線程.
f)如果有人知道更好的解決方案,請隨時通知我們:o)
一個)
訂閱者將在另一台機器上,所以我認為tcp://
是解決方案。*
當然,這里是NP。 { pgm:// | epgm:// | tipc:// }
{ pgm:// | epgm:// | tipc:// }
如果進一步進入更高性能水平的方向,可能會在這里很有趣
b)
訂閱者將通過XPUB
套接字將消息轉發給其他訂閱者。PUSH/PULL
可以工作,但如果我想將這些訂閱及其過濾器傳遞給初始發布者並在源頭過濾一些消息,我必須使用PUB/SUB
模式。
好吧,O / P中沒有提到。 XPUB
s/ XSUB
的任何分層都可以正常工作,問題出在連接管理級別
C)
澄清一下,只有當有訂閱者時,不丟失消息才重要。 你能再解釋一下這部分嗎?
當然,如果在 RTO 連接的鏈接上沒有可用的訂閱者,准備好“通過網絡”立即交付,則永遠無法交付任何消息(並且可能會默默地丟棄,這是您試圖與之抗爭的,不是嗎? ?)。 這就是zmq.IMMEDIATE
可以通過調用.setsockopt()
方法來管理的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.