zmq 發佈/訂閱模式的詳解 python代碼

發佈/訂閱模式的特點:
1.一個發佈者,多個訂閱者的關係,1:n;
2.當發佈者數據變化時發佈數據,所有訂閱者均能夠接收到數據並處理。
這就是發佈/訂閱模式。
在這裏插入圖片描述

  1. 使用SUB設置一個訂閱時,必須使用zmq_setsockopt()對消息進行過濾,例如:

服務端代碼

import zmq
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect("tcp://localhost:5555")   #多個客戶端連接同樣的地址
socket.setsockopt(zmq.SUBSCRIBE,'123'.encode('utf-8'))  # 消息過濾  只接受123開頭的信息
#socket.setsockopt(zmq.SUBSCRIBE,''.encode('utf-8'))  # 當zmq_setsockopt()的第二個參數設置爲空時,表示不過濾任何消息
while True:
    response = socket.recv().decode('utf-8');
    print("response: %s" % response)

這是另外一種消息的過濾方法 (訂閱郵編默認10001)

# Subscribe to zipcode, default is NYC, 10001
zip_filter = sys.argv[1] if len(sys.argv) > 1 else "10001"

# Python 2 - ascii bytes to unicode str
if isinstance(zip_filter, bytes):
    zip_filter = zip_filter.decode('ascii')
socket.setsockopt_string(zmq.SUBSCRIBE, zip_filter)

setsockopt的詳細使用

http://api.zeromq.org/3-2:zmq-setsockopt

客戶端代碼

import zmq
import sys
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:5555")

while True:
    msg = input("請輸入要發佈的信息:").strip()
    if msg == 'b':   #關閉連接的字符
        sys.exit()
    socket.send(msg.encode('utf-8'))
    time.sleep(1)
  1. PUB-SUB模式是異步的
    訂閱者調用zmq.send()來發送消息是會報錯的,同樣發佈者使用zmq.recv()來接收消息也會報錯。

  2. PUB和SUB誰bind誰connect並無嚴格要求(雖本質並無區別),但仍建議PUB使用bind,SUB使用connect

  3. 一個訂閱者(subcriber)可以鏈接超過一個發佈者(publisher)。數據到達後將交叉存取(公平隊列),以保證發佈者之間的數據不會淹沒。
    連接多個發佈者來接收需要的消息:

import sys
import zmq

#  Socket to talk to server
context = zmq.Context()
socket = context.socket(zmq.SUB)

print("Collecting updates from weather server...")
socket.connect("tcp://localhost:5556")
socket.connect("tcp://localhost:5557")

# Subscribe to zipcode, default is NYC, 10001
zip_filter = sys.argv[1] if len(sys.argv) > 1 else "10001"

# Python 2 - ascii bytes to unicode str
if isinstance(zip_filter, bytes):
    zip_filter = zip_filter.decode('ascii')
socket.setsockopt_string(zmq.SUBSCRIBE, zip_filter)

while True:
    string = socket.recv_string()
    #zipcode, temperature, relhumidity = string.split()
    print(string)

可以在發佈者發送信息的開頭來區分不同發佈者發送的信息,客戶端通過

subscriber.setsockopt(zmq.SUBSCRIBE, b"A")

來判斷接收指定的發佈者的數據

當訂閱者訂閱了不同類型的發佈信息,爲了保證互相不阻塞 有兩種方法

import zmq
import time

# Prepare our context and sockets
context = zmq.Context()

# Connect to task ventilator
receiver = context.socket(zmq.PULL)
receiver.connect("tcp://localhost:5557")

# Connect to weather server
subscriber = context.socket(zmq.SUB)
subscriber.connect("tcp://localhost:5556")
subscriber.setsockopt(zmq.SUBSCRIBE, b"10001")

# Process messages from both sockets
# We prioritize traffic from the task ventilator
while True:

    # Process any waiting tasks
    while True:
        try:
            msg = receiver.recv(zmq.DONTWAIT)
        except zmq.Again:
            break
        # process task

    # Process any waiting weather updates
    while True:
        try:
            msg = subscriber.recv(zmq.DONTWAIT)
        except zmq.Again:
            break
        # process weather update
        
    time.sleep(0.01)

import zmq

# Prepare our context and sockets
context = zmq.Context()

# Connect to task ventilator
receiver = context.socket(zmq.PULL)
receiver.connect("tcp://localhost:5557")

# Connect to weather server
subscriber = context.socket(zmq.SUB)
subscriber.connect("tcp://localhost:5556")
subscriber.setsockopt(zmq.SUBSCRIBE, b"10001")

# Initialize poll set
poller = zmq.Poller()
poller.register(receiver, zmq.POLLIN)
poller.register(subscriber, zmq.POLLIN)

# Process messages from both sockets
while True:
    try:
        socks = dict(poller.poll())
    except KeyboardInterrupt:
        break

    if receiver in socks:
        message = receiver.recv()
        # process task

    if subscriber in socks:
        message = subscriber.recv()
        # process weather update
  1. 如果一個發佈者(publisher)沒有任何訂閱者(subcriber)連接,則發佈者會簡單的丟棄所有的消息。
  2. 如果使用TCP同時訂閱者(subcriber)很慢,這會導致消息在發佈者(publisher)端排隊,造成消息堆積影響程序性能,爲了解決這個問題,需要合理設置high-water mark(高水位線)。
    定義:
    ZeroMQ使用HWM(高水位標誌)的概念來定義其內部管道的容量。從套接字或進入套接字的每個連接都有自己的管道和用於發送和/或接收的HWM,具體取決於套接字類型。一些套接字(發佈、推送)只有發送緩衝區。一些(SUB, PULL, REQ, REP)只有接收緩衝區。一些(經銷商,路由器,對)有發送和接收緩衝區。
    當您的套接字到達其HWM時,它將根據套接字類型阻塞或刪除數據。如果PUB和ROUTER套接字到達它們的HWM,它們將丟棄數據,而其他套接字類型將阻塞。

要注意的是,PUB、ROUTER套接字在到達HWM後會丟棄數據,其他的會阻塞。另外,inproc的transport,發送端和接收端共享同一個緩存,因此實際HWM是兩者HWM之和。
下面是對必須處理未知訂閱方的發佈方來說的一個更明智的“最佳實踐”:

總是給套接字設置一個基於期望的訂閱方數量的最大值,你打算用於隊列的內存的數量,和一個消息平均大小的高水位線。例如,如果你希望有5000個訂閱方,有1G的內存可有,消息平均200字節,那麼一個安全的高水位線應該是(1000000000/200/5000)=1000.

  1. 從ZeroMQ v3.x版本開始,使用tcp://或者ipc://協議連接時會在發佈者進行消息過濾,使用epgm://協議仍在訂閱者過濾;在ZeroMQ v2.x,所有消息過濾都發生在訂閱者。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章