[英]Asynchronous Client/Server pattern in Python ZeroMQ
我有3個用Python編寫的程序,需要連接。 2個程序X和Y收集一些信息,這些信息由它們發送到程序Z。程序Z分析數據並將一些決定發送給程序X和Y。 將來將擴展與X和Y類似的程序數量。 最初,我使用命名管道來允許從X,Y到Z的通信。但是如您所見,我需要雙向關系。 我的老板告訴我使用ZeroMQ。 我剛剛為我的用例找到了模式,稱為異步客戶端/服務器。 請參閱下面的ZMQ書( http://zguide.zeromq.org/py:all )中的代碼。
問題是我的老板不想使用任何線程,分支等。我將客戶端和服務器任務移到了單獨的程序中,但是我不確定該如何處理ServerWorker類。 可以在沒有線程的情況下使用它嗎? 另外,我想知道如何確定最佳工人數量。
import zmq
import sys
import threading
import time
from random import randint, random
__author__ = "Felipe Cruz <felipecruz@loogica.net>"
__license__ = "MIT/X11"
def tprint(msg):
"""like print, but won't get newlines confused with multiple threads"""
sys.stdout.write(msg + '\n')
sys.stdout.flush()
class ClientTask(threading.Thread):
"""ClientTask"""
def __init__(self, id):
self.id = id
threading.Thread.__init__ (self)
def run(self):
context = zmq.Context()
socket = context.socket(zmq.DEALER)
identity = u'worker-%d' % self.id
socket.identity = identity.encode('ascii')
socket.connect('tcp://localhost:5570')
print('Client %s started' % (identity))
poll = zmq.Poller()
poll.register(socket, zmq.POLLIN)
reqs = 0
while True:
reqs = reqs + 1
print('Req #%d sent..' % (reqs))
socket.send_string(u'request #%d' % (reqs))
for i in range(5):
sockets = dict(poll.poll(1000))
if socket in sockets:
msg = socket.recv()
tprint('Client %s received: %s' % (identity, msg))
socket.close()
context.term()
class ServerTask(threading.Thread):
"""ServerTask"""
def __init__(self):
threading.Thread.__init__ (self)
def run(self):
context = zmq.Context()
frontend = context.socket(zmq.ROUTER)
frontend.bind('tcp://*:5570')
backend = context.socket(zmq.DEALER)
backend.bind('inproc://backend')
workers = []
for i in range(5):
worker = ServerWorker(context)
worker.start()
workers.append(worker)
poll = zmq.Poller()
poll.register(frontend, zmq.POLLIN)
poll.register(backend, zmq.POLLIN)
while True:
sockets = dict(poll.poll())
if frontend in sockets:
ident, msg = frontend.recv_multipart()
tprint('Server received %s id %s' % (msg, ident))
backend.send_multipart([ident, msg])
if backend in sockets:
ident, msg = backend.recv_multipart()
tprint('Sending to frontend %s id %s' % (msg, ident))
frontend.send_multipart([ident, msg])
frontend.close()
backend.close()
context.term()
class ServerWorker(threading.Thread):
"""ServerWorker"""
def __init__(self, context):
threading.Thread.__init__ (self)
self.context = context
def run(self):
worker = self.context.socket(zmq.DEALER)
worker.connect('inproc://backend')
tprint('Worker started')
while True:
ident, msg = worker.recv_multipart()
tprint('Worker received %s from %s' % (msg, ident))
replies = randint(0,4)
for i in range(replies):
time.sleep(1. / (randint(1,10)))
worker.send_multipart([ident, msg])
worker.close()
def main():
"""main function"""
server = ServerTask()
server.start()
for i in range(3):
client = ClientTask(i)
client.start()
server.join()
if __name__ == "__main__":
main()
因此,您從這里獲取了代碼: 異步客戶端/服務器模式
請密切注意顯示該代碼所針對的模型的圖像。 特別是,請查看“圖38-異步服務器的詳細信息”。 ServerWorker
類正在ServerWorker
5個“ Worker”節點。 在代碼中,那些節點是線程,但是您可以使它們完全獨立於程序。 在這種情況下,您的服務器程序(可能)將不會對其進行旋轉,它們將單獨旋轉,並僅與您的服務器進行通信以表明它們已准備好接受工作。
您會在ZMQ示例中經常看到這種情況,ZMQ示例是在單個可執行文件中的線程中模擬的多節點拓撲。 僅僅是為了使閱讀整篇文章容易,並不總是打算那樣使用。
對於您的特殊情況,讓工作人員成為線程或將其分解為單獨的程序是很有意義的……但是,如果老板提出了業務要求,則只需將其分解為單獨的程序即可。
當然,要回答第二個問題,在不了解他們將要執行的工作量以及他們將需要多快響應的情況下,無法知道有多少工人是最佳的……您的目標是讓工人完成工作比收到新工作快。 在很多情況下,只有一個工人即可實現這一目標。 如果是這樣,您可以讓服務器本身成為工作服務器,而只需跳過架構的整個“工作人員層”。 為了簡單起見,您應該從此處開始,然后進行一些負載測試以查看它是否實際上可以有效地處理您的工作負載。 如果不是,則了解完成一項任務需要多長時間,以及完成任務有多快。假設某個工人可以在15秒內完成一項任務。 每分鍾4個任務。 如果每分鍾要執行5個任務,那么您需要2個工作人員,並且還有一點增長空間。 如果情況千差萬別,那么您就必須決定資源與可靠性。
在您走得更遠之前,請確保您已閱讀了第4章,可靠的請求/答復模式,它將為處理異常提供一些見識,並可能為您提供更好的模式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.