NSQ是一个消息队列,其设计的目的如下(顺序无关):
- 支持高可用和单点故障(SPOF)的拓扑
- 满足健壮的消息传递保障需求
- 限制内存
- 对消息生产者和消费者都足够简单的配置
- 简单的升级路径
- 高效
简化配置和管理
只需要一个nsqd
实例就可以同时处理多个数据流。数据流在NSQ中叫做topic
,每一个topic
有一个或多个channel
。每一个channel
接收来自一个topic
的所有消息的拷贝。在实践中,一个channel
对应于一个topic
的下游消费者。
topic和channel不是同时被配置的:
- 当上游生产者第一次发布消息到命名topic,或下游channel第一次订阅命名topic时,会创建一个对应的topic。
- 当消费者订阅命名channel时,会创建一个对应的channel。
每个topic和channel之间都拥有相互独立的缓存队列,放置一个慢消费者造成整个系统的积压。
每个channel通常有多个消费者。假设所有的消费者都可以接收消息,每一条消息会被随机发送到一个消费者,如下:
topic,channel和consumer
在topic->channel之间,消息是广播的,但在channel和consumer之间,消息是分布式发送的。即每一个channel都从topic接收全量的消息,但每一个customer只从channel接收部分消息。
NSQ包含一个助手应用nsqlookupd
,为消费者查找感兴趣的topic的的nsqd实例地址提供目录服务。这个服务让生产者和消费者解耦,相互独立,通过nsqlookupd
来关联,降低了系统的复杂度。
在底层实现中,每一个nsqd实例和nsqlookupd之间维持了一条TCP长连接,周期性的发送它目前的状态。nsqlookupd使用该信息决策消费者应该使用哪个nsqd实例。
给一个topic添加一个新的消费者,只要启动一个配置了nsqlookupd
地址的NSQ客户端即可。无需再针对生产者和消费者做任何配置,一切都由nsqlookupd
来完成。
需要注意的是nsqd和nsqlookupd都是独立部署的,即多个nsqd之间没有关联,多个nsqlookupd之间也没有关联。
nsqadmin
是用来监控和管理集群的工具,提供了web界面来浏览topcs/channels/consumers的拓扑和关键信息,并且可以执行删除或清空channel等任务。
排除单点故障
NSQ被设计用于分布式环境。nsqd客户端通过TCP和所有提供相同topic的生产者连接。所以其中没有中间人,没有消息代理,也不存在单点故障。
对nsqlookupd来说,通过运行多个实例来达到高可用的目的。多个nsqlookupd之间不会通信,数据被认为是最终一致的。消费者查询它配置的所有nsqlookupd实例,并将返回值合并,得到最终结果。
消息传递保障
NSQ保证每条消息至少被传递成功一次,可能会重复传递多次。消费者应该对收到的消息做去重处理,或者保证收到消息后的处理是无副作用的。
消息保证的工作机制如下(设计客户端已经成功连接并订阅了一个topic):
- 客户端指示已经准备好接收消息
- NSQ发送消息,并临时存储在本地(在重传时使用)
- 客户端回复FIN或者REQ消息来指示消息接收成功或者失败。如果客户端没有回复消息,NSQ会在一定时间超时后,自动重传该消息。
上述机制保证了导致消息丢失的唯一场景是nsqd实例的意外宕机,所有在内存中还未发送的消息,以及需要重传的消息都会丢失。
如果防止消息丢失是重中之重的事情,针对上述场景也可以做一些事情。一种方式是配置冗余的nsqd组,来传递重复的消息。
限制内存使用
nsqd提供了一个配置选项--mem-queue-size
,决定一个队列保存在内存中的消息数量。如果队列中消息数量超过阈值,消息会被写入到磁盘。这样,就先知了一个nsqd进程的内存使用量为mem-queue-size*#_of_channels_and_topics
。
通过将该选项设置的比较小(0或者1),也能得到更为可靠的消息传递服务。持久化到磁盘的消息在nsqd进程重启后依然有效。
高效
NSQ基于类memcached的通信机制实现,客户端只需要回复其容量信息。所有消息数据(包括尝试次数、时间戳等元数据)都保存在核心上。这样消除了重传时服务器和客户端之间消息的来回拷贝。同时也简化了客户端实现,不需要再维持消息状态。
在数据协议方面,为了最大化性能和吞吐量的一个关键设计是直接向客户端推送数据,而不是等待客户端来查询。这个概念被称为RDY
状态,本质上是客户端侧的流控。
当一个客户端连接到nsqd,并订阅到一个channel时,它的RDY状态被置为0,表示目前还不能接收消息。当客户端准备好接收消息时,它会发送一条命令到nsqd,更新其RDY值到它可以接收的消息数量。nsqd就可以将100条消息推送给客户端,中间不需要其他的任何交互。
网友评论