组件来去自如,ZQM会负责自动重连,服务端和客户端可以随意的退出网络。tcp的话,必须先有服务端启动,再启动客户端,否则会报错。
ZMQ会在必要的情况下将消息放入队列中保存,一旦建立了连接就开始发送。
ZMQ有阈值机制,当队列满的时候,可以自动阻塞发送者,或者丢弃部分消息。
ZMQ可以使用不同的通信协议进行连接,TCP,进程间,线程间。
ZMQ提供了多种模式进行消息路由。如请求-应答模式,发布-订阅模式等,这些模式可以用来搭建网络拓扑结构。
ZMQ会在后台线程异步的处理I/O操作,他使用一种不会死锁的数据结构来存储消息。
(1). 使用Request-Reply模式,需要遵循一定的规律。
(2).客户端必须先发送消息,再接收消息;服务端必须先进行接收客户端发送过来的消息,再发送应答给客户端,如此循环
(3). 服务端和客户端谁先启动,效果都是一样的。
(4). 服务端在收到消息之前,会一直阻塞,等待客户端连上来。
示例
创建一个客户端和服务端,客户端发送消息给服务端,服务端返回消息给客户端,客户端和服务器谁先启动都可以
client.py
import zmq
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:7777")
# 客户端必须要先发送消息,然后在接收消息
if __name__ == '__main__':
for i in range(1, 10):
socket.send_string("hello")
message = socket.recv()
print(f'received reply message: {message}')
server.py
import zmq
import time
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:7777")
count = 0
# 必须要先接收消息,然后在应答
if __name__ == '__main__':
print('zmq server start....')
while True:
message = socket.recv()
count += 1
print(f'received request.message: {message}, count: {count}')
time.sleep(1)
socket.send_string('world!')
常见数据请求发送API:
#发送数据
socket.send_json(data) #data 会被json序列化后进行传输 (json.dumps)
socket.send_string(data, encoding="utf-8") #data为unicode字符串,会进行编码成子节再传输
socket.send_pyobj(obj) #obj为python对象,采用pickle进行序列化后传输
socket.send_multipart(msg_parts) # msg_parts, 发送多条消息组成的迭代器序列,每条消息是子节类型,如[b"message1", b"message2", b"message2"]
#接收数据
socket.recv_json()
socket.recv_string()
socket.recv_pyobj()
socket.recv_multipart()
import zmq
import time
import random
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:7777")
if __name__ == '__main__':
print("发布者启动......")
time.sleep(2)
for i in range(1000):
temp = random.randint(-10, 40)
message = "我是publisher, 这是我发布给你们的第{}个消息!今日温度{}".format(i+1, temp)
socket.send_string(message)
Subscriber.py
import zmq
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect("tcp://localhost:7777")
#客户端需要设定一个过滤,否则收不到任何信息
socket.setsockopt_string(zmq.SUBSCRIBE, '')
if __name__ == '__main__':
print("订阅者一号启动......")
while True:
message = socket.recv_string()
print(f"(订阅者一号)接收到'发布者'发送的消息:{message}")
import zmq
import random
raw_input = input
context = zmq.Context()
sender = context.socket(zmq.PUSH)
sender.bind("tcp://*:7777")
sink = context.socket(zmq.PUSH)
sink.connect("tcp://localhost:7778")
if __name__ == '__main__':
# 同步操作
print("Press Enter when the workers are ready: ")
_ = raw_input()
print("sending tasks to workers...")
sink.send_string('0')
# 发送十个任务
total_time = 0
for task_one in range(10):
# 每个任务耗时为N
workload = random.randint(1, 5)
total_time += workload
sender.send_string(str(workload))
print(f"10个任务的总工作量: {total_time} 秒")
Worker.py (中间处理)
import zmq
import time
context = zmq.Context()
receiver = context.socket(zmq.PULL)
receiver.connect("tcp://localhost:7777")
sender = context.socket(zmq.PUSH)
sender.connect("tcp://localhost:7778")
if __name__ == '__main__':
while True:
s = receiver.recv_string()
print(f'work1 接收到一个任务...需要{s}秒')
# Do the work
time.sleep(int(s))
# send results to sink
sender.send_string(f'work1完成一个任务,耗时{s} 秒')
Sink.py 结果接收器
import zmq
import time
context = zmq.Context()
receiver = context.socket(zmq.PULL)
receiver.bind("tcp://*:7778")
if __name__ == '__main__':
s = receiver.recv_string()
print('开始接收处理结果...')
# 计时,所有任务处理完一共需要多久
start = time.time()
# 接收十个任务的处理结果
for task_one in range(10):
s = receiver.recv_string()
print(s)
print(f"三个worker同时工作,耗时: {time.time() - start}秒")
zmq.Poller(ZMQ超时判断)
socket_req_url = config.zmq_rep_host.format(config.zmq_rep_port)
socket_req = zmq.Context().socket(zmq.REQ)
socket_req.connect(socket_req_url)
poller = zmq.Poller()
poller.register(socket_req, zmq.POLLIN)
超时重连实例:
Server服务端:
import zmq
import time
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
count = 0
#必须要先接收消息,然后在应答
if __name__ == '__main__':
print('zmq server start....')
while True:
message = socket.recv()
count += 1
print('received request. message:{} count:{}'.format(message, count))
time.sleep(1)
socket.send_string("ping test suc")
Client客户端:
import zmq
#超时重连
class PingPort():
def __init__(self):
self.port = '5555'
self.socket_req_url = 'tcp://localhost:{}'.format(self.port)
self.socket_req = zmq.Context().socket(zmq.REQ)
self.socket_req.connect(self.socket_req_url)
self.poller = zmq.Poller()
self.poller.register(self.socket_req, zmq.POLLIN)
def ping(self):
self.socket_req.send_string('ping test')
if self.poller.poll(3000):
resp = self.socket_req.recv()
print(resp)
return True
else:
print('ping {} port fail, no response.'.format(self.port))
self.socket_req.setsockopt(zmq.LINGER, 0)
self.socket_req.close()
self.poller.unregister(self.socket_req)
print('-------------开始重连--------------------')
self.socket_req = zmq.Context().socket(zmq.REQ)
self.socket_req.connect(self.socket_req_url)
self.poller = zmq.Poller()
self.poller.register(self.socket_req, zmq.POLLIN)
self.ping()
if __name__ == '__main__':
obj = PingPort()
print(obj.ping)