详解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 ORM框架SQLAlchemy学习笔记之关系映射实例
Jun 10 Python
python实现超简单端口转发的方法
Mar 13 Python
在Django的视图(View)外使用Session的方法
Jul 23 Python
Python更新数据库脚本两种方法及对比介绍
Jul 27 Python
Python实现带参数的用户验证功能装饰器示例
Dec 14 Python
Python字符串通过'+'和join函数拼接新字符串的性能测试比较
Mar 05 Python
python打开windows应用程序的实例
Jun 28 Python
pytorch下大型数据集(大型图片)的导入方式
Jan 08 Python
Python flask路由间传递变量实例详解
Jun 03 Python
Django后端分离 使用element-ui文件上传方式
Jul 12 Python
使用Django的JsonResponse返回数据的实现
Jan 15 Python
Python爬虫之爬取某文库文档数据
Apr 21 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 验证码实例代码
2010/06/01 PHP
浅谈php错误提示及查错方法
2015/07/14 PHP
PHPExcel导出2003和2007的excel文档功能示例
2017/01/04 PHP
Lumen timezone 时区设置方法(慢了8个小时)
2018/01/20 PHP
PHP基于openssl实现的非对称加密操作示例
2019/01/11 PHP
关于laravel后台模板laravel-admin select框的使用详解
2019/10/03 PHP
php测试kafka项目示例
2020/02/06 PHP
PHP实现文件上传后台处理脚本
2020/03/04 PHP
再谈IE中Flash控件的自动激活 ObjectWrap
2007/03/09 Javascript
javaScript矢量图表库-gRaphael几行代码实现精美的条形图/饼图/点图/曲线图
2013/01/09 Javascript
JS 操作Array数组的方法及属性实例解析
2014/01/08 Javascript
22点关于jquery性能优化的建议
2014/05/28 Javascript
开源的javascript项目Kissy介绍
2014/11/28 Javascript
jQuery中add()方法用法实例
2015/01/08 Javascript
JS实现带提示的星级评分效果完整实例
2015/10/30 Javascript
js print打印网页指定区域内容的简单实例
2016/11/01 Javascript
JavaScript数组和对象的复制
2017/03/21 Javascript
jquery select插件异步实时搜索实例代码
2017/10/20 jQuery
vue2.0s中eventBus实现兄弟组件通信的示例代码
2017/10/25 Javascript
微信小程序异步处理详解
2017/11/10 Javascript
jQuery zTree树插件的使用教程
2019/08/16 jQuery
基于javascript处理nginx请求过程详解
2020/07/07 Javascript
详解Python中open()函数指定文件打开方式的用法
2016/06/04 Python
Python实现输出程序执行进度百分比的方法
2017/09/16 Python
python实现超简单的视频对象提取功能
2018/06/04 Python
对Python中数组的几种使用方法总结
2018/06/28 Python
Python3安装psycopy2以及遇到问题解决方法
2019/07/03 Python
django将数组传递给前台模板的方法
2019/08/06 Python
python中pickle模块浅析
2020/12/29 Python
Ann Taylor官方网站:美国最大的女性产品制造商之一
2016/09/14 全球购物
建筑安全标语
2014/06/07 职场文书
物业前台接待岗位职责
2015/04/03 职场文书
整改通知书
2015/04/20 职场文书
无罪辩护词范文
2015/05/21 职场文书
青年教师听课心得体会
2016/01/15 职场文书
关于Spring配置文件加载方式变化引发的异常详解
2022/01/18 Java/Android