ZeroMQ及其模式

刚刚这个国庆,对程序员来说,最糟心的事情莫过于 ZeroMQ 的作者 Pieter Hintjens 的安乐死。想必你的朋友圈也传过了那篇令人感怀的 A protocal for dying。如果你还没看,翻翻朋友圈,仔细读一读,然后收藏起来,一两年后再看上一看。可敬的 Pieter,临终前的 last words,也不放过自己搞 messaging 的本行,借用了 Alice 和 Bob(https://en.wikipedia.org/wiki/Alice_and_Bob )调侃了一番。

我对 Pieter 其实并不了解太多,和他之间的唯一桥梁就是 ZeroMQ。平心而论,ZeroMQ 是个很出色的,broker-less(相对于 RabbitMQ,Kafka 这样的 broker)的 messaging lib(注意不是 message queue,ZeroMQ的名字有些误导),API 很友好(socket),效率很高(Pull/Push 100 Byte 的message,~2M/s,见https://github.com/zeromq/jeromq/wiki/Performance ),可伸缩性很强(Pub/Sub 下可以有 10k 的 subscriber),然而它也有其显著的缺点:令人诟病的 zmq_ctx,以及其设计上有缺陷的Concurrency model(http://zeromq.org/whitepapers:architecture )。当然,我写这篇文章并非为了褒扬或者贬低 ZeroMQ,也不是要普及 ZeroMQ,给大家做个入门(ZeroMQ 入门比较简单),而是想谈谈 ZeroMQ 里面那些宝贵的通讯系统设计的思想,或者说模式,这些模式曾经对我的影响还是不小的。

基础知识

在讲一些思想之前,我们需要先了解一些概念。

一个 messaging system 最重要的事情莫过于消息送达的模式:at least once 或者 at most once。at least once 是指同一个消息会被传输 1 到 n 次,而 at most once 是指同一个消息会被传输 0 到 1 次。这很好理解,如果 messaging system 内建了重传机制,并且将消息持久化到磁盘中以保证即便进程崩溃消息依旧能够送达,那么这就是 at least once。反之,如果没有构建任何上述的机制,消息送出后就并不理会,这是 at most once。很可惜,ZeroMQ 并非严格意义上的 at least once 或者 at most once,以其 Pub/Sub 模式来说,ZeroMQ 构建了消息确认和重传机制,却未对消息进行持久化,那么内存耗尽或者进程崩溃都会造成消息丢失,而重传则可能会造成消息被发送 1 到 n 次。这也是为何我认为 ZeroMQ 并非真正意义上的 Message Queue,当然,它可以用来构建一个真正的 MQ。注意,在一个网络环境中,消息的送达只能是上述两种情况,不可能 exactly once,如果有人这么说,那么一定是在误导。

讲到消息重传,细心的同学可能会疑虑:TCP 内建有重传机制,为何 ZeroMQ 在消息层面还要多此一举?这是因为 TCP 的重传只保证了网络层面报文的重传,而 ZeroMQ 通过消息层面的重传,保证了一个消息一旦送达,一定是完整送达。

at least once 的使用场景很容易理解,我们发送一条消息,自然是为了接受者能够保证接收到。至于保证接收的副作用 —— 重传的副本,只要消息的处理是幂等(Idempotent)的,就不会有问题。at most once 的使用场景让人比较困惑,什么时候我们发了一条消息,丢了也就丢了,并不可惜呢?比如说这些场景:

  • 各种网络拓扑下的 heart beat(当然,大部分场合下 heart beat 可以直接用 IP/UDP,不必使用 messaging),偶尔丢几个消息无关痛痒
  • 密集的 status report message 或者 tracking event。丢失的消息对全局并不构成威胁。

最后一个概念是 back pressure。但凡一个 messaging system 里,消息的两端,生产者和消费者间,都会产生处理速度不一致的问题。如果生产者发送消息的速度过快,消费者赶不及处理,就会造成消息的拥塞,进而不断把压力回溯给上游,最终一层层回溯到消息的生产者,使其停止产生更多的内容。

消息通讯的模式

搞定了一些基础知识后,我们看 ZeroMQ 涉及到的一些消息通讯的模式。

REQ/REP

REQ/REP 是最基本的模式。客户端发送数据请求服务器的响应。

PUB/SUB

Pub/Sub 是消息传输非常常见也是非常有用的一种模式,它和 observer pattern 师出同门,将数据的发布者和订阅者解耦 —— 发布者者只管产生数据,而不必关心谁是订阅者,有多少订阅者。比如说你要建一个聊天室,每个人都是发布者,也都是订阅者。发布者不必关心订阅者的加入和离开,消息会以 1:N 的方式扩散到每个订阅者。

PUB/SUB (forward proxy)

Pub/Sub 自身组合使用可以解决很多实际问题。比如你有很多数据要发布给内部应用和外部应用使用,而外部应用可以访问的数据是内部应用的一个子集。通过组合 Pub/Sub,让其中一个(或者多个)订阅者在收到数据后,过滤出想要对外发布的 topic(或者 channel),然后再重新发布出去,供外网的应用订阅。

Push/Pull (map reduce)

Push/Pull 是消息传输的另一个重要的 pattern。Push/Pull 的特点是无论是 Push 端还是 Pull 端都可以做 server,bind 到某个地址等待对方访问。如果我们在 Push 端绑定地址,那么这是一个 Push server,对应的 Pull clients 可以 connect 到这个 Push server 往外拉数据;反之,如果我们建立一个 Pull server,对应的 Push clients 可以 connect 到这个 Pull server 往里压数据。由此,我们可以轻松实现一个 task 的 map reduce 的 framework。如上图所示,中间的 worker 可以随需增减。

如果我们更进一步,当所有的 task aggregate(reduce)完毕后,我们想终止所有的 worker,可以在这个系统里加上 Pub/Sub 的机制:sink 进行控制信息的发布,所有的 worker 订阅这个 channel,在收到 SIGTERM 后,结束自己的进程。

Push/Pull (fair queue)

Push/Pull 模式的另外一个应用场景是 fair queue — Push clients 轮番往 Pull server 写入数据。

Router/Dealer

Router/Dealer 模式是典型的 broker 模式。在多对多的网络中, 掮客起到在网络的两端双方互不认识的情况下,促成双方的交易。超市就是一个典型的掮客。顾客不必和所有的供应商一一打交道,每个供应商也不需要认识所有的顾客来促成交易 —— 整个交易在超市的促成下完成,双方几乎都不知道对方的存在。

多对多的网络中,Router/Dealer 模式很有用。假设我们有 N 个 Reply server,M 个 Request client,若要保证高可用性,正常而言,双方需要一个 M x N 的 full mesh 的网络才能保证任何一个 client 能够和任何一个 server 建立连接。通过在中间加一层 Router/Dealer,M x N 的连接被简化成 M + N。网络的复杂度大大降低。

综合

结合 Pub/Sub,Router/Dealer,Pull/Push等模式,我们可以很容易支撑非常复杂的网络应用,如上图所示。

先讲这么多,对此感兴趣的同学可以看 ZeroMQ 的官方 guide,Pieter 亲自操刀撰写的,非常赞,即使你不打算在你的应用中使用 ZeroMQ,但读读这个文档也是非常有益的。下次我把今天就这个话题在 TubiTV 做的 BBL的 slides 和 video 放上来,有兴趣的同学可以看看。

原文发布于微信公众号 - 程序人生(programmer_life)

原文发表时间:2016-10-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

打印机安全研究(一):不容乐观的网络打印机安全状况

打印机是人们在生活和办公中经常使用的电子设备,家庭、办公室、公司、政府单位、医院、学校......几乎每一个单位和机构都会使用打印机。从安全的角度来看,由于打印...

3485
来自专栏SDNLAB

SDNLAB技术分享(八):Neutron的基本原理与代码实现

一、Openstack网络基础 下面对Openstack和Neutron的介绍,要从几个关键词入手。 1. 三代网络 在网络这一口,OpenStack经历了由n...

3637
来自专栏数据和云

高可用失灵:交换机导致Oracle集群故障致机场停运

最近日本的一则数据库故障引发了全日航空(ANA)航班停运,被广泛关注。 据日本《产经新闻》3月22日报道,日本全日空航空公司(ANA)的国内航班系统22日8时...

3365
来自专栏跟着阿笨一起玩NET

C#中的串口通信

串行接口按电气标准及协议来分,包括RS-232-C、RS-422、RS485、USB等。 RS-232-C、RS-422与RS-485标准只对接口的电气特性做出...

4262
来自专栏微信终端开发团队的专栏

微信终端跨平台组件 mars 系列(二):信令传输超时设计

本篇文章将为大家介绍 STN(信令传输网络模块),由于 STN 的复杂性,该模块将被分解为多个篇章进行介绍,本文主要内容为微信中关于读写超时的思考与设计。

6020
来自专栏小白课代表

犀牛Rhino6.1安装教程

1753
来自专栏Linux驱动

31.Linux-wm9876声卡驱动(移植+测试)

本节学习目的 1)分析Linux中的OSS声卡系统 2)移植wm9876声卡 3)使用madplay应用程序播放mp3 1.声音三要素 采样频率 音频采样率是指...

4686
来自专栏zaking's

真正“搞”懂http协议01—背景故事

1253
来自专栏无原型不设计

优秀原型设计欣赏:阅读类App原型制作-Another Read

Another Read是一款阅读学习App,它主要是带给你最具创意的作家与插图画家出版的儿童读物。在这款App中,你可以根据喜好来左滑或者右滑书籍,喜欢的书籍...

2495
来自专栏逍遥剑客的游戏开发

一个困扰我一个多星期的Nebula3的BUG

1703

扫码关注云+社区

领取腾讯云代金券