我有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()
答案 0 :(得分:2)
所以,你从这里抓取代码:Asynchronous Client/Server Pattern
密切关注显示此代码所针对的模型的图像。特别是,请查看&#34;图38 - 异步服务器的详细信息&#34;。 ServerWorker
班正在激活5&#34;工人&#34;节点。在代码中,这些节点是线程,但您可以将它们完全分开。在这种情况下,您的服务器程序(可能)不会负责将它们旋转起来,它们会单独启动,只是与您的服务器通信他们已准备好接收工作。
您经常会在ZMQ示例中看到这一点,这是一个多节点拓扑,模仿单个可执行文件中的线程。只是让阅读整个事情变得简单,并不总是打算以这种方式使用。
对于您的特定情况,让工人成为线程或将其分解为单独的程序是有意义的......但如果它是您老板的业务需求,那么只需将它们分解为单独的程序即可
当然,要回答您的第二个问题,如果不了解他们将要执行的工作量以及他们需要多快响应的速度,就无法知道有多少工作人员是最优的......你的目标是让工人比收到新工作更快地完成工作。在许多情况下,可以通过单个工作人员实现这一目标。如果是这样,您可以让您的服务器本身成为工作者,并且只需跳过整个&#34;工作层&#34;的架构。为了简单起见,您应该从那里开始,然后进行一些负载测试,看看它是否能真正有效地应对您的工作负载。如果没有,请了解完成任务需要多长时间,以及任务进入的速度。让我们说工人可以在15秒内完成任务。那一分钟的4个任务。如果每分钟完成5项任务,那么你需要2名工人,并且你会有一点成长空间。如果事情变化很大,那么你就必须做出资源与可靠性的决定。
在你走得太远之前,请务必阅读第4章“可靠请求/回复模式”,它将为处理异常提供一些见解,并可能为您提供更好的模式。