發佈/訂閱模式的特點:
1.一個發佈者,多個訂閱者的關係,1:n;
2.當發佈者數據變化時發佈數據,所有訂閱者均能夠接收到數據並處理。
這就是發佈/訂閱模式。
- 使用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)
-
PUB-SUB模式是異步的
訂閱者調用zmq.send()來發送消息是會報錯的,同樣發佈者使用zmq.recv()來接收消息也會報錯。 -
PUB和SUB誰bind誰connect並無嚴格要求(雖本質並無區別),但仍建議PUB使用bind,SUB使用connect
-
一個訂閱者(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
- 如果一個發佈者(publisher)沒有任何訂閱者(subcriber)連接,則發佈者會簡單的丟棄所有的消息。
- 如果使用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.
- 從ZeroMQ v3.x版本開始,使用tcp://或者ipc://協議連接時會在發佈者進行消息過濾,使用epgm://協議仍在訂閱者過濾;在ZeroMQ v2.x,所有消息過濾都發生在訂閱者。