nsq 基本介绍
nsqd是一个负责接收、排队、转发消息到客户端的守护进程;
nsqlookupd是一个管理集群(nsqd)拓扑信息并提供最终一致性的服务注册与发现的守护进程;
nsqadmin是一个管理界面,提供实时的集群中topic、channel和message的统计信息,还提供与这些实体相关的各种管理接口。
核心概念
message即代表的是数据,它被生产者创建并发送到指定的topic,而消费者可以从指定的topic和channel接收并消费消息。且每个被nsqd接收到的消息至少会被发送一次给消费者,因为消息消费超时会触发重入队过程,且消息附带有一个投递次数属性,以使得客户端对投递次数过大的消息灵活处理。
topic代表生产者投递消息的一个逻辑键值,它可以将消息进行分类。一个nsqd上可包含多个topic,且它们可以不必在生产者投递消息前就创建,换言之,topic可在其第一次接收到生产者投递的消息时创建。
channel代表消费者订阅某个nsqd上的topic的消息。你可以仍旧将它视作一个消息队列,只不过它与消费者相关。每当生产者将消息发布到一个topic上,消息会被拷贝(深拷贝,即构建一个消息副本)到与topic关联的所有的channel。而且,多个消费者可以订阅同一个channel,channel会将其接收到的消息随机(即随机负载均衡)发送到与其关联的一个客户端。同topic类似,channel也可不用提前创建,消费者在第一次订阅消息(在指定topic的某个channel)的时候会创建此channel(若其不存在)。最后,channel从topic接收的消息首先会在内存中排队,当达到内存队列长度上限,就被写到持久化存储。
关键特性
nsq官方所声称的去中心化分布式特征,指的是,因为各节点(nsqd甚至也包括nsqlookupd)之间不会产生状态(数据)共享或依赖,因此可以直接通过添加节点来提升系统处理能力(即系统可通过水平扩展来来线性提升处理能力),且单个节点宕机不会影响其它节点的功能。因此,正是因为节点之间没有状态共享,没有数据冗余或副本的概念,使得它也不需要使用复杂的一致性算法来保证数据的一致;
虽然官方声称其是具体可靠消息传递,但是需要注意的是,其的确也存在消息丢失的情形,因为虽然它通过提供消息持久化来缓解这一问题,但并没有根本解决这一问题,一旦宕机,内存中的消息会丢失,当然你可以将内存队列长度mem_queue_size参数设置地更小一些以缓解此问题。甚至,你可以节点作冗余操作,以保证数据丢失的可能性在实际生产中几乎不可能发生(注意不是不可能发生),但对于那些需要高可靠性的消息发布的应用场景,nsq是无法保证的。
还有,官方文档提到一点,nsq并没有kafaka那么强大,它不能保证消息的严格顺序,换言之,生产者创建的消息可以随时(不确定性)地以任何顺序进入到nsqd的消息队列。典型地,官方推荐将消息生产者与nsqd实例协同部署,即部署在同一台机器上,这样即使发生网络分区,也不会影响生产者消息的投递,明显在此种情况下消息投递的效率理论上会被其它情况下的消息投递效率。但官方提供了一种解决方案,即将消息打上时间戳。尽管如此,它仍然不适合需要保证严格消息顺序的情况下。
nsq保证的消息至少一次会被发送给生产者。换言之,消息可能被多次发送给消费者。因此,消费者应该能够识别消息重复(message de-duplicate),或者保证消息所涉及的操作幂等性(idempotent)。更具体地,造成消息被多次发送的原因包括,客户端连接断开或消息超时时间内未返回响应,这些都会导致消息的重入队操作(requeue)。
关于nsqlookupd,其也是可水平扩展的,但各节点实例之间也是没有任何联系的,结合业务逻辑来阐述,即每个nsqdlookupd实例可分别接收部分或全部的nsqd实例的服务注册请求,这只需要客户端在连接到nsqlookupd获取所有的生产者(及topic和channel)信息后,对获取的数据进行一个union操作即可,便可得到整个系统中生产者的拓扑信息(这即是官方文档中所描述的将发现服务设计成eventually consistent)。
最后,不得不说的一点是,正是因为上面所描述的各种不足,造就了nsq的精简设计。简单实在是太重要了!因为简单意味着容易进行故障或bug查找,同时,容易部署和使用方便。不得不说,当满足了应用的基本功能之后,简单往往是最重要的一个因素。