详解Python 实现 ZeroMQ 的三种基本工作模式


Posted in Python onMarch 24, 2020

简介

引用官方说法:ZMQ(以下 ZeroMQ 简称 ZMQ)是一个简单好用的传输层,像框架一样的一个 socket library,他使得 Socket 编程更加简单、简洁和性能更高。

是一个消息处理队列库,可在多个线程、内核和主机盒之间弹性伸缩。

ZMQ 的明确目标是“成为标准网络协议栈的一部分,之后进入 Linux 内核”。现在还未看到它们的成功。但是,它无疑是极具前景的、并且是人们更加需要的“传统” BSD 套接字之上的一 层封装。ZMQ 让编写高性能网络应用程序极为简单和有趣。

它跟 RabbitMQ,ActiveMQ 之类有着相当本质的区别,ZeroMQ 根本就不是一个消息队列服务器,更像是一组底层网络通讯库,对原有的 Socket API 加上一层封装,使我们操作更简便。

三种工作模式

Request-Reply 模式:

说到“请求-应答”模式,不得不说的就是它的消息流动模型。消息流动模型指的是该模式下,必须严格遵守“一问一答”的方式。

发出消息后,若没有收到回复,再发出第二条消息时就会抛出异常。同样的,对于 Rep 也是,在没有接收到消息前,不允许发出消息。

基于此构成“一问一答”的响应模式。

server:

# -*- coding=utf-8 -*-

import zmq

context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")

while True:
 message = socket.recv()
 print("Received: %s" % message)
 socket.send("I am OK!")

client:

# -*- coding=utf-8 -*-

import zmq
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")

socket.send('Are you OK?')
response = socket.recv()
print("response: %s" % response)

Publish-Subscribe 模式:

“发布-订阅”模式下,“发布者”绑定一个指定的地址,例如“192.168.10.1:5500”,“订阅者”连接到该地址。该模式下消息流是单向的,只允许从“发布者”流向“订阅者”。且“发布者”只管发消息,不理会是否存在“订阅者”。一个“发布者”可以拥有多个订阅者,同样的,一个“订阅者”也可订阅多个发布者。

虽然我们知道“发布者”在发送消息时是不关心“订阅者”的存在于否,所以先启动“发布者”,再启动“订阅者”是很容易导致部分消息丢失的。那么可能会提出一个说法“我先启动‘订阅者',再启动‘发布者',就能解决这个问题了?”

对于 ZeroMQ 而言,这种做法也并不能保证 100% 的可靠性。在 ZeroMQ 领域中,有一个叫做“慢木匠”的术语,就是说即使我是先启动了“订阅者”,再启动“发布者”,“订阅者”总是会丢失第一批数据。因为在“订阅者”与端点建立 TCP 连接时,会包含几毫秒的握手时间,虽然时间短,但是是存在的。再加上 ZeroMQ 后台 IO 是以一部方式执行的,所以若不在双方之间施加同步策略,消息丢失是不可避免的。

关于“发布-订阅”模式在 ZeroMQ 中的一些其他特点:

  • 公平排队,一个“订阅者”连接到多个发布者时,会均衡的从每个“发布者”读取消息,不会出现一个“发布者”淹没其他“发布者”的情况。
  • ZMQ3.0 以上的版本,过滤规则发生在“发布方”。 ZMQ3.0 以下的版本,过滤规则发生在“订阅方”。其实也就是处理消息的位置。

server:

# -*- coding=utf-8 -*-

import zmq
import time

context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:5555")

for i in range(10):
 print('send message...' + str(i))
 socket.send('message' + str(i))
 time.sleep(1)

client:

# -*- coding=utf-8 -*-

import zmq

context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect("tcp://localhost:5555")
socket.setsockopt(zmq.SUBSCRIBE, '')
while True:
 response = socket.recv()
 print("response: %s" % response)

Parallel Pipeline 模式:

在说明“管道模式”前,需要明确的是在 ZeroMQ 中并没有绝对的服务端与客户端之分,所有的数据接收与发送都是以连接为单位的,只区分 ZeroMQ 定义的类型。就像套接字绑定地址时,可以使用 bind ,也可以使用 connect ,只是通常我们将理解中的服务端 bind 到一个地址,而理解中的客户端 connec 到该地址。

“管道模式”一般用于任务分发与结果收集,由一个任务发生器来产生任务,“公平”的派发到其管辖下的所有 worker,完成后再由结果收集器来回收任务的执行结果。

整体流程比较好理解,worker 连接到任务发生器上,等待任务的产生,完成后将结果发送至结果收集器。如果要以客户端服务端的概念来区分,这里的任务发生器与结果收集器是服务端,而 worker 是客户端。

前面说到了这里任务的派发是“公平的”,因为内部采用了 LRU 的算法来找到最近最久未工作的闲置 worker。但是公平在这里是相对的,当任务发生器启动后,第一个连接到它的 worker 会在一瞬间承受整个任务发生器产生的 tasks。

总结来说由三部分组成,push 进行数据推送,work 进行数据缓存,pull 进行数据竞争获取处理。区别于 Publish-Subscribe 存在一个数据缓存和处理负载。

当连接被断开,数据不会丢失,重连后数据继续发送到对端。

server:

# -*- coding=utf-8 -*-

import zmq
import time

context = zmq.Context()
socket = context.socket(zmq.PUSH)
socket.bind("tcp://*:5557")

for i in range(10):
 socket.send('message' + str(i))
 # 没启 worker 时不会发消息
 print('send message...' + str(i))
 time.sleep(1)

work:

# -*- coding=utf-8 -*-
import zmq
context = zmq.Context()
receive = context.socket(zmq.PULL)
receive.connect('tcp://127.0.0.1:5557')

sender = context.socket(zmq.PUSH)
sender.connect('tcp://127.0.0.1:5558')

while True:
 data = receive.recv()
 print('transform...' + data)
 sender.send(data)

client:

# -*- coding=utf-8 -*-
import zmq

context = zmq.Context()
socket = context.socket(zmq.PULL)
socket.bind("tcp://*:5558")

while True:
 response = socket.recv()
 print("response: %s" % response)

以上。

参考文档:

总结

到此这篇关于详解Python 实现 ZeroMQ 的三种基本工作模式的文章就介绍到这了,更多相关python ZeroMQ工作模式内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
全面解读Python Web开发框架Django
Jun 30 Python
python实现颜色rgb和hex相互转换的函数
Mar 19 Python
python类继承用法实例分析
May 27 Python
Django入门使用示例
Dec 12 Python
python数据抓取分析的示例代码(python + mongodb)
Dec 25 Python
python互斥锁、加锁、同步机制、异步通信知识总结
Feb 11 Python
python dataframe 输出结果整行显示的方法
Jun 14 Python
Python网络爬虫之爬取微博热搜
Apr 18 Python
Django获取该数据的上一条和下一条方法
Aug 12 Python
使用python和pygame制作挡板弹球游戏
Dec 03 Python
python kafka 多线程消费者&手动提交实例
Dec 21 Python
Python实现文字pdf转换图片pdf效果
Apr 03 Python
python使用梯度下降算法实现一个多线性回归
Mar 24 #Python
PyQt5+python3+pycharm开发环境配置教程
Mar 24 #Python
python实现最速下降法
Mar 24 #Python
python实现梯度法 python最速下降法
Mar 24 #Python
PyQt5+Pycharm安装和配置图文教程详解
Mar 24 #Python
python实现梯度下降法
Mar 24 #Python
pycharm下配置pyqt5的教程(anaconda虚拟环境下+tensorflow)
Mar 25 #Python
You might like
php 带逗号千位符数字的处理方法
2012/01/10 PHP
PHP中的魔术方法总结和使用实例
2015/05/11 PHP
PHP-FPM之Chroot执行环境详解
2015/08/03 PHP
Laravel5.1框架注册中间件的三种场景详解
2019/07/09 PHP
javascript使用location.search的示例
2013/11/05 Javascript
Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法总结
2013/11/14 Javascript
jQuery实现tab标签自动切换的方法
2015/02/28 Javascript
完美兼容多浏览器的js判断图片路径代码汇总
2015/04/17 Javascript
JavaScript中pop()方法的使用教程
2015/06/09 Javascript
学习JavaScript设计模式之代理模式
2016/01/12 Javascript
浅析JavaScript中的变量复制、参数传递和作用域链
2016/01/13 Javascript
jQuery easyUI datagrid 增加求和统计行的实现代码
2016/06/01 Javascript
checkbox 选中一个另一个checkbox也会选中的实现代码
2016/07/09 Javascript
bootstrap3-dialog-master模态框使用详解
2017/08/22 Javascript
js中怎么判断两个字符串相等的实例
2019/01/17 Javascript
React 组件渲染和更新的实现代码示例
2019/02/21 Javascript
关于vue状态过渡transition不起作用的原因解决
2019/04/09 Javascript
countup.js实现数字动态叠加效果
2019/10/17 Javascript
ES6学习笔记之let与const用法实例分析
2020/01/22 Javascript
python抓取某汽车网数据解析html存入excel示例
2013/12/04 Python
python中enumerate的用法实例解析
2014/08/18 Python
python实现颜色rgb和hex相互转换的函数
2015/03/19 Python
在Python中操作字典之fromkeys()方法的使用
2015/05/21 Python
Django之路由层的实现
2019/09/09 Python
Python实现定时监测网站运行状态的示例代码
2020/09/30 Python
Ajax和javascript的区别
2013/07/20 面试题
技术副厂长岗位职责
2013/12/26 职场文书
银行员工辞职信范文
2014/01/20 职场文书
小加工厂管理制度
2014/01/21 职场文书
计算机应届毕业生自荐信范文
2014/02/23 职场文书
个人授权委托书
2014/09/15 职场文书
营销与策划实训报告
2014/11/05 职场文书
庐山导游词
2015/02/03 职场文书
红旗渠导游词
2015/02/09 职场文书
分享Python异步爬取知乎热榜
2022/04/12 Python
Android开发手册Chip监听及ChipGroup监听
2022/06/10 Java/Android