详解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 json encode datetime类型
Dec 28 Python
简单介绍Python中的struct模块
Apr 28 Python
Nginx搭建HTTPS服务器和强制使用HTTPS访问的方法
Aug 16 Python
VScode编写第一个Python程序HelloWorld步骤
Apr 06 Python
Python解决两个整数相除只得到整数部分的实例
Nov 10 Python
Python3实现取图片中特定的像素替换指定的颜色示例
Jan 24 Python
DataFrame:通过SparkSql将scala类转为DataFrame的方法
Jan 29 Python
Python计算一个点到所有点的欧式距离实现方法
Jul 04 Python
python3实现网页版raspberry pi(树莓派)小车控制
Feb 12 Python
Python itertools.product方法代码实例
Mar 27 Python
Python检测端口IP字符串是否合法
Jun 05 Python
Python调用飞书发送消息的示例
Nov 10 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
中国收音机工业发展史
2021/03/02 无线电
PHP 中检查或过滤IP地址的实现代码
2011/11/27 PHP
CodeIgniter输出中文乱码的两种解决办法
2014/06/12 PHP
PHP命名空间(namespace)的动态访问及使用技巧
2014/08/18 PHP
thinkphp判断访客为手机端或PC端的方法
2014/11/24 PHP
一个判断email合法性的函数[非正则]
2008/12/09 Javascript
用js调用迅雷下载代码的二种方法
2013/04/15 Javascript
JS判断数组中是否有重复值得三种实用方法
2013/08/16 Javascript
thinkphp 表名 大小写 窍门
2015/02/01 Javascript
分享使用AngularJS创建应用的5个框架
2015/12/05 Javascript
实例介绍JavaScript中多种组合继承
2019/01/20 Javascript
JavaScript运动原理基础知识详解
2020/04/02 Javascript
详解vuejs中执行npm run dev出现页面cannot GET/问题
2020/04/26 Javascript
angular组件间传值测试的方法详解
2020/05/07 Javascript
JQuery实现折叠式菜单的详细代码
2020/06/03 jQuery
浅谈JavaScript 声明提升
2020/09/14 Javascript
使用python删除nginx缓存文件示例(python文件操作)
2014/03/26 Python
django模型中的字段和model名显示为中文小技巧分享
2014/11/18 Python
python用模块zlib压缩与解压字符串和文件的方法
2016/12/16 Python
python与C互相调用的方法详解
2017/07/14 Python
Python引用传值概念与用法实例小结
2017/10/07 Python
用Python分析3天破10亿的《我不是药神》到底神在哪?
2018/07/12 Python
python实现图片批量压缩程序
2018/07/23 Python
python 将json数据提取转化为txt的方法
2018/10/26 Python
对python函数签名的方法详解
2019/01/22 Python
python GUI库图形界面开发之PyQt5计数器控件QSpinBox详细使用方法与实例
2020/02/28 Python
基于Python爬取素材网站音频文件
2020/10/21 Python
HTML5未来发展趋势
2016/02/01 HTML / CSS
受欢迎的大学生自我评价
2013/12/05 职场文书
超市后勤自我鉴定
2014/01/17 职场文书
个人简历中的自我评价怎么写
2014/01/26 职场文书
开学典礼主持词
2014/03/19 职场文书
《彩色世界》教学反思
2014/04/12 职场文书
2015年实习单位评语
2015/03/25 职场文书
千手观音观后感
2015/06/03 职场文书
如何用threejs实现实时多边形折射
2021/05/07 Javascript