乐趣区

关于后端:九神带你理解消息队列MQ

MQ 的定义

MQ 是英文 Message Queue 的缩写,翻译成中文是音讯队列。所谓队列,是根底数据结构中“先进先出”的一种数据结构。而音讯队列,是指在不同服务之间流传音讯。

MQ 存在的意义

为什么须要 MQ?

技术上任何货色存在要有价值,就必须有应用的场景。咱们思考一个日常的场景,你在网上 A 商城买货色,下单付款,这时候 A 给你推送了一条短信,告之你付费购买了 XX;并且 A 还给你发了邮件,告之你付费购买了 XX。

在上述情景中,假如 A 的交易体量很大,所以下单付款和发短信、发邮件属于不同的服务。每当下单胜利了,订单服务就要向外发消息,让发短信服务和发邮件服务去把短信和邮件收回去。

这时候就须要在服务之间流传音讯,所以须要 MQ。

流传音讯间接 HTTP 不行么?为什么要用 MQ?

服务之间间接应用 HTTP 协定流传音讯,只能抉择 RESTful 的接口来实现。这种接口方式,对于音讯队列的传输来说,太重了。所以并不是说这种方法做不了,而是面向 API 的接口开发,要么每一次接口改变就去批改代码,这很不不便。而且,你如果想晓得短信有没有收回去,就必须让短信服务再给你回一个接口,代码量就很夸大。

这个时候,不如专门抽出来一个中间件去关怀短信有没有收回去、订单服务收回的音讯是否反复、订单服务收回的音讯是否程序正确这种事件。此时业务服务自身只须要关怀发和承受音讯就好了。

所以,为了不便企业 IT 零碎外部服务之间的通信,咱们就须要独自把这个事件拿进去做成一个中间件——即音讯队列,一方面承受音讯,一方面把音讯收回去。这样的益处是能把与音讯相干的技术一并处理,还能升高服务之间的耦合性。

MQ 只是单纯的服务之间流传音讯么?

MQ 的外围性能是服务之间流传音讯,然而为了保障音讯流传的牢靠,咱们须要思考诸如 RPC、高可用、程序和反复音讯、牢靠投递、生产关系解析等等这类问题。

MQ 的理论应用

当你须要应用音讯队列时,首先须要思考它的必要性。能够应用 mq 的场景有很多,最罕用的几种,是做业务解耦 / 最终一致性 / 播送 / 错峰流控等。反之,如果须要强一致性,关注业务逻辑的处理结果,则 RPC 显得更为适合。

解耦

解耦是音讯队列要解决的最实质问题。所谓解耦,简略点讲就是一个事务,只关怀外围的流程。而须要依赖其余零碎但不那么重要的事件,有告诉即可,无需期待后果。换句话说,基于音讯的模型,关怀的是“告诉”,而非“解决”。

比方下面的例子中,上游的订单服务其实只关怀 Orders 表里有没有正确的插入数据,基本不关怀上游的短信、邮件服务的音讯处理结果。

正如方才剖析的,如果上游的短信、邮件服务定时来拉取数据,也能保证数据的更新,只是实时性没有那么强。但应用接口方式去发短信、邮件,显然对于这些服务来说太过于“重量级”了,只须要公布一个订单创立的告诉,由上游零碎来解决,可能更为正当。

再举一个例子,公司的经营必定心愿能看到每天的订单数据,从而进行剖析。然而订单零碎显著也不关怀这种业务,这时候抉择发一个告诉给音讯核心让上游的展现零碎让他们去实现本人的业务,是最好的抉择了。

最终一致性

最终一致性指的是两个零碎的状态保持一致,要么都胜利,要么都失败。当然有个工夫限度,实践上越快越好,但实际上在各种异样的状况下,可能会有肯定提早达到最终统一状态,但最初两个零碎的状态是一样的。

业界有一些为“最终一致性”而生的音讯队列,如 Notify(阿里)、QMQ(去哪儿)等,其设计初衷,就是为了交易系统中的高牢靠告诉。

以一个银行的转账过程来了解最终一致性,转账的需要很简略,如果 A 零碎扣钱胜利,则 B 零碎加钱肯定胜利。反之则一起回滚,像什么都没产生一样。

然而,这个过程中存在很多可能的意外:

  1. A 扣钱胜利,调用 B 加钱接口失败。
  2. A 扣钱胜利,调用 B 加钱接口尽管胜利,但获取最终后果时网络异样引起超时。
  3. A 扣钱胜利,B 加钱失败,A 想回滚扣的钱,但 A 机器 down 机。

可见,想把这件看似简略的事真正做成,真的不那么容易。所有跨 VM 的一致性问题,从技术的角度讲通用的解决方案是:

  1. 强一致性,分布式事务,但落地太难且老本太高,后文会具体提到。
  2. 最终一致性,次要是用“记录”和“弥补”的形式。在做所有的不确定的事件之前,先把事件记录下来,而后去做不确定的事件,后果可能是:胜利、失败或是不确定,“不确定”(例如超时等)能够等价为失败。胜利就能够把记录的货色清理掉了,对于失败和不确定,能够依附定时工作等形式把所有失败的事件从新搞一遍,直到胜利为止。
  3. 回到方才的例子,零碎在 A 扣钱胜利的状况下,把要给 B“告诉”这件事记录在库里(为了保障最高的可靠性能够把告诉 B 零碎加钱和扣钱胜利这两件事保护在一个本地事务里),告诉胜利则删除这条记录,告诉失败或不确定则依附定时工作补偿性地告诉咱们,直到咱们把状态更新成正确的为止。
  4. 整个这个模型仍然能够基于 RPC 来做,但能够形象成一个对立的模型,基于音讯队列来做一个“企业总线”。
  5. 具体来说,本地事务保护业务变动和告诉音讯,一起落地(失败则一起回滚),而后 RPC 达到 broker,在 broker 胜利落地后,RPC 返回胜利,本地音讯能够删除。否则本地音讯始终靠定时工作轮询一直重发,这样就保障了音讯牢靠落地 broker。
  6. broker 往 consumer 发送音讯的过程相似,始终发送音讯,直到 consumer 发送生产胜利确认。
  7. 咱们先不理睬反复音讯的问题,通过两次音讯落地加弥补,上游是肯定能够收到音讯的。而后依赖状态机版本号等形式做判重,更新本人的业务,就实现了最终一致性。

最终一致性不是音讯队列的必备个性,但的确能够依附音讯队列来做最终一致性的事件。另外,所有不保障 100% 不丢音讯的音讯队列,实践上无奈实现最终一致性。好吧,应该说实践上的 100%,排除零碎重大故障和 bug。

像 Kafka 一类的设计,在设计层面上就有丢音讯的可能(比方定时刷盘,如果掉电就会丢音讯)。哪怕只丢千分之一的音讯,业务也必须用其余的伎俩来保障后果正确。

播送

音讯队列的基本功能之一是进行播送。如果没有音讯队列,每当一个新的业务方接入,咱们都要联调一次新接口。有了音讯队列,咱们只须要关怀音讯是否送达了队列,至于谁心愿订阅,是上游的事件,无疑极大地缩小了开发和联调的工作量。

比方本文开始提到的产品核心公布产品变更的音讯,以及景点库很多去重更新的音讯,可能“关怀”方有很多个,但产品核心和景点库只须要公布变更音讯即可,谁关怀谁接入。

错峰与流控

试想上下游对于事件的解决能力是不同的。比方,Web 前端每秒接受上千万的申请,并不是什么神奇的事件,只须要加多一点机器,再搭建一些 LVS 负载平衡设施和 Nginx 等即可。但数据库的解决能力却非常无限,即便应用 SSD 加分库分表,单机的解决能力依然在万级。因为老本的思考,咱们不能奢求数据库的机器数量追上前端。

这种问题同样存在于零碎和零碎之间,如短信零碎可能因为短板效应,速度卡在网关上(每秒几百次申请),跟前端的并发量不是一个数量级。但用户早晨个半分钟左右收到短信,个别是不会有太大问题的。如果没有音讯队列,两个零碎之间通过协商、滑动窗口等简单的计划也不是说不能实现。但零碎复杂性指数级增长,势必在上游或者上游做存储,并且要解决定时、拥塞等一系列问题。而且每当有解决能力有差距的时候,都须要独自开发一套逻辑来保护这套逻辑。所以,利用两头零碎转储两个零碎的通信内容,并在上游零碎有能力解决这些音讯的时候,再解决这些音讯,是一套绝对较通用的形式。

总而言之,音讯队列不是万能的。对于须要强事务保障而且提早敏感的,RPC 是优于音讯队列的。

对于一些无关痛痒,或者对于他人十分重要然而对于本人不是那么关怀的事件,能够利用音讯队列去做。

反对最终一致性的音讯队列,可能用来解决提早不那么敏感的“分布式事务”场景,而且绝对于轻便的分布式事务,可能是更优的解决形式。

当上下游零碎解决能力存在差距的时候,利用音讯队列做一个通用的“漏斗”。在上游有能力解决的时候,再进行散发。

如果上游有很多零碎关怀你的零碎收回的告诉的时候,果决地应用音讯队列吧。

MQ 的流派划分

MQ 的次要流派分为两种,一种是有 Broker 的 MQ,一种是没有 Broker 的 MQ。没有 Broker 的 MQ 能够看成一次 RPC,有 Broker 的 MQ 能够看成是把一次 RPC 变成 2 次或者 3 次 RPC。

有 Broker 的 MQ

这个流派通常有一台服务器作为 Broker,所有的音讯都通过它直达。生产者把音讯发送给它就完结本人的工作了,Broker 则把音讯被动推送给消费者或者让消费者从 Broker 拉取。

一个音讯队列装备 Broker,无外乎要做两件事件:

  1. 音讯的转储,在更适合的工夫点投递,或者通过一系列伎俩辅助音讯最终能送达消费机。
  2. 标准一种范式和通用的模式,以满足解耦、最终一致性、错峰等需要。

这种 MQ 的代表有 kafka、RabbitMQ、JMS。因为本篇文章并不具体讲某个 MQ,所以仅简略说一下最火的 kafka,如下图:

kafka 是一个公布订阅音讯零碎,由 topic 辨别音讯品种,每个 topic 中能够有多个 partition,每个 kafka 集群有一个多个 broker 服务器组成,producer 能够公布音讯到 kafka 中,consumer 能够生产 kafka 中的数据。kafka 就是生产者和消费者两头的一个暂存区,能够保留一段时间的数据保障应用。zk 在 kafka 中做元数据存储以及 partition 的 failover。在下一个大的 kafka 版本中,zk 将会被去除。

无 Broker 的 MQ

这种流派的 MQ 就是单纯的把音讯发送看成是一次 RPC 了,次要的代表有 ZeroMQ。

文章首发于:
九神带你了解音讯队列 MQ

退出移动版