Go 消息队列介绍
Go 消息队列介绍
消息队列/MQ 是日常开发中经常用到的,在一些比较复杂的系统中加入消息中间件能够带来极大的解耦、强扩展性、容错性。其实消息队列的本质数据结构就是队列,生产者往队列里面生产数据,消费者消费队列里面的数据。但一个可供线上生产使用的消息中间件要考虑更多的东西,比如消息在消费过程中丢失了怎么办?消息的存储备份怎么做?消息是存储在磁盘中还是内存里?是基于push模式还是基于pull模式?集群该怎么实现等等这些问题都是一个成熟的消息队列要考虑的。开源的消息队列有很多,其本质上差不多,但特性和侧重点以及功能完备程度则不同。NSQ 是 Go 语言开发的成熟消息队列,同时相比kafka它足够轻量,代码也很清晰,本章会从功能特性到源码实现来分析NSQ的实现,希望能让大家对消息中间件的理解更加深刻。本文先来介绍一下NSQ的主要特性以及同另一个著名MQ kafka的对比。
为什么要使用消息队列
解耦
用户下一个订单可能涉及到多个系统,如果按照传统写法要在代码中同步的调用多个系统的API,比如调用库存系统更新库存,调用推荐系统更新用户画像,调用会员积分系统更新用户的积分等等。如果其中某一系统某一时间段挂了将会导致整个请求失败,并且这些调用代码都耦合写在一起那维护起来会非常困难。而通过消息队列来解耦多个系统能够带来更好的扩展性和维护性。参照图片可能更好理解:
异步
对于一些可以异步处理的逻辑完全可以放在消息队列里面让下游处理,并不关心和依赖本次执行调用的结果,这样的逻辑放在消息队列里去异步处理是极为合适的(比如发送邮件只需要把邮件消息丢到消息队列里面就可以直接返回或者接着处理下面的逻辑了,后续消费线程消费消息队列发送邮件即可),可以大大提升系统的处理能力,否则系统就得同步的傻傻的等待一个自己并不关心的执行调用完之后才能向用户返回。同样结合图片来理解:
缓冲
对于流量的突然暴增,消息队列有很好的削峰缓冲的作用,防止打崩系统,因为一个系统所承载的流量并不是时时刻刻都是一样的,可能某个时刻用户集中请求过来,但某个时候请求比较少,但一般一个系统所能承载的
QPS
是有限的,如果不采用消息队列来缓冲就有可能在高峰流量期系统承受不住而宕机。
以上是使用消息队列最主要的三个原因和收益,当然消息队列还有其他的作用,比如存储、广播等等,总之消息队列是各个系统间的“中介”,是传输过程中消息的容器,消息队列在复杂一些的系统中扮演了很重要的角色。
NSQ 是什么
之前的篇幅主要介绍了消息队列是什么以及使用消息队列的优势,NSQ
就是一个标准的消息队列,并且它足够轻量、易于理解、具备基本的消息队列功能、使用Golang
开发。
NSQ
是使用Go
语言开发的成熟的轻量级消息队列,核心的消息队列功能都有实现,但相对于kafka
等重型MQ
来说它的功能可能没有那么完备。举个例子,kafka
单是提供的配置项就有几十上百个,而NSQ
提供的配置项不过十来个而已,而且大多数使用默认配置即可,无需更改,只有比较特殊的场景可能需要额外配置。NSQ
具有分布式和去中心化拓扑结构,该结构具有无单点故障、故障容错、高可用性以及保证消息可靠传递等特征。接下来我们就通过对比NSQ
和kafka
的特性来对NSQ
有一个基本的理解。
NSQ VS kafka
存储方式:
NSQ
的存储结合内存和磁盘,提供配置项来配置内存消息最大数量,超出则会存储到磁盘上。kafka
则是直接消息都存储到磁盘上,同时做了大量的优化来保证磁盘读写的性能,有兴趣可以阅读Kafka - Design File System来看看kafka
为什么要设计成磁盘存储并且如何做到高性能的。
消费模式:
NSQ
基于push
模式,一有消息就会直接push
给下游消费端。kafka
基于pull
模式,需要消费者主动去pull
消息。
一般来说
push
模式具有更高的实时性同时避免消费者空pull
的问题,但可能会出现消费者的消费速度已经落后还在不停的给消费者push
的情况,因此需要一些额外的工作来实现流量控制。消费顺序
NSQ
的消费顺序是无法保证的。kafka
能够保证单partition
内的消息被顺序消费。
消息消费保证
NSQ
保证生产投递的消息最少被消费一次(at least once)kafka
除了支持最少一次之外还在一定程度上支持了准确一次(Exactly once),这个实现机制比较复杂,有兴趣的可以看看Exactly-once Semantics are Possible: Here’s How Kafka Does it
数据备份
kafka
通过partition
的机制对消息做了备份,进一步增强了消息的安全性。
以上几个就是NSQ
和kafka
在功能设计上的最大的不同,不仅仅限于NSQ
和 kafka
,只要是消息队列都需要在以上几个基点问题上进行考虑和选择,同时由于kafka
花了更多力气去做了partion
之间的数据备份保证数据安全,更多的消息消费保证模式以及顺序消费的支持等使得它的系统复杂性要远高于NSQ
。其实有赞也有对NSQ
开源版本的一个改良版本也是借鉴了kafka
对这些问题的解决思路有赞NSQ,支持了顺序消费和数据备份的问题。此外还有一张MQ
比对图:
总结
本文介绍了消息队列的概念和用处,对比了NSQ
和kafka
功能特性,可以看到虽然各个消息队列的本质是一样的,但具体到功能特性和实现上面还是会有很大不同的,如果对各个消息队列的功能特性都比较熟悉的话在日常工作中的技术选型上是有很大作用的,不同场景下选择适合的消息队列才是王道。下一篇文章我们就来看一下NSQ
的主要概念、架构设计,为阅读NSQ
的源码做准备。
<p> <span style="font-size:14px;">本专刊是Go开源项目源码分析专栏,共 17 篇文章,挑选了Go 开源界知名的 4 个开源项目gnet(高效的网络库)、gin(知名的Go微型web框架)、fasthttp(高性能web框架)、nsq(Go消息队列)来对它们进行源码分析,分析它们的设计思想和代码实现。每个项目的讲解都是由浅入深,由设计思想的剖析到源码实现的分析,更易于读者理解。</span> </p> <p> <br /> </p> <h2> <b><span style="font-size:16px;line-height:1;">购买须知:</span></b> </h2> <span style="font-size:14px;">订阅成功后,用户即可通过牛客网 PC 端、App 端享有永久阅读的权限;</span><br /> <span style="font-size:14px;">牛客专刊为虚拟内容服务,订阅成功后概不退款;</span><br /> <span style="font-size:14px;line-height:1;">在专刊阅</span><span style="font-size:14px;line-height:1;">读过程中,如有任何问题,可在文章评论区底部留言,或添加牛客导师,加入读者交流群;</span><br /> <span style="font-size:14px;">想成为牛客作者,请邮件联系yinxiaoxiao@nowcoder.com,邮件主题【牛客作者+写作方向】,并附上个人简历一份及近期作品一份;</span><br /> <p> <span style="font-size:14px;">牛客专刊版权归本平台所有,任何机构、媒体、网站或个人未经本网协议授权不得转载、链接、转贴或以其他方式复制发布 / 发表,违者将依法追究责任</span><span style="font-size:14px;">。</span> </p> <p> <br /> </p>