消息队列及其模式

转自:https://zhuanlan.zhihu.com/p/22947038
刚刚这个国庆,对程序员来说,最糟心的事情莫过于 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 放上来,有兴趣的同学可以看看。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容

  • 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅的消息系统。主要设计目标如下: 以时间复杂度为O...
    高广超阅读 12,712评论 8 167
  • 拯救世界 略 开始的假设 我们假设你使用ZeroMQ 3.2以上的版本。我们假设你使用Linux或者类似的操作系统...
    lakerszhy阅读 11,067评论 1 14
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 1 消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,...
    Bobby0322阅读 10,803评论 0 24
  • 本文转载自http://dataunion.org/?p=9307 背景介绍Kafka简介Kafka是一种分布式的...
    Bottle丶Fish阅读 5,355评论 0 34