我有一个pyzmq Publisher,它每秒发送大约1000条消息。我试图在异步event_loop中启动大约10个订户。
它可以工作,但比唯一一个订阅者的速度慢约2.5倍。
代码可能有什么问题?
import asyncio
import zmq
import json
from zmq.backend.cython.constants import NOBLOCK
from zmq.asyncio import Context, Poller
from loop_ import Loop
class Client:
REQUEST_TIMEOUT = 35000
SERVER_ENDPOINT = "tcp://localhost:6666"
def __init__(self, id_):
self.id = id_
def get_task(self):
return asyncio.create_task(self.client_coroutine())
async def client_coroutine(self):
context = Context.instance()
socket = context.socket(zmq.SUB)
socket.connect(self.SERVER_ENDPOINT)
socket.setsockopt(zmq.SUBSCRIBE, b'4')
poller = Poller()
poller.register(socket, zmq.POLLIN)
while True:
event = dict(await poller.poll(self.REQUEST_TIMEOUT))
if event.get(socket) == zmq.POLLIN:
reply = await socket.recv_multipart(flags=NOBLOCK)
if not reply:
break
else:
print(eval(json.loads(reply[1].decode('utf-8'))))
else:
print("No response from server, retrying...")
socket.setsockopt(zmq.LINGER, 0)
socket.close()
poller.unregister(socket)
async def tasks():
_tasks = [Client(id_).get_task() for id_ in range(10)]
done, pending = await asyncio.wait(_tasks, return_when=asyncio.FIRST_EXCEPTION)
loop = asyncio.get_event_loop()
loop.run_until_complete(tasks())
答案 0 :(得分:1)
Q :代码可能有什么问题?
鉴于代码使用的是相同的 localhost
(从使用地址可以看到),可疑的第一位是,要处理的工作量增加了10倍,此类工作量将始终强调localhost
的操作系统和CPU,不是吗?
接下来是运输级别的选择。鉴于所有 SUB
-和 localhost
共同位于同一 PUB
,所有基于L3堆栈的TCP / IP协议工作都浪费了。要比较相对成本(为此硬件单一消息传递使用 tcp://
传输类的附加效果),请使用 {{1} } 传输类,其中不会进行与协议相关的TCP / IP堆栈附加处理。
最后但并非最不重要的一点是,我的代码将永远不会混合使用不同的事件循环(自v2.11起使用ZeroMQ,因此有人可能会认为我有点过时了,避免依赖于inproc://
装饰的功能最近py3.6 +)
我的代码将像 async
中那样,对每个aSocketINSTANCE
使用显式,非阻塞,零等待测试,以检查是否存在消息,而不是使用任何“外部”添加了修饰,可能会报告相同的修饰,但是通过一些附加的操作(昂贵且在我的控制代码范围之外)进行事件处理。所有实时,低延迟的用例都力图承担尽可能小的延迟/开销,因此,在任何“现代”语法糖化技巧下,使用显式控制始终会在我的项目中胜出。
无论如何,享受零的禅宗