nsq
一、nsq基础指南
1、nsq介绍
NSQ是分布式实时消息队列。NSQ是分布式的、拓扑结构,具有无单点故障、故障容错、高可用性和保证消息的可靠传递等特点,容易配置和部署。
2、nsq组件
组件/概念 | 端口 |
说明 |
---|---|---|
nsqd |
TCP端口: 4150 HTTP端口:4151 |
nsq的核心,负责消息的存储与分发。包括topic和channel的管理、producer和consumer的维护,简单的说,真正干活的就是这个服务. |
nsqlookupd |
TCP端口: 4160 HTTP端口:4161 |
主要功能是服务发现。每个nsqd启动时都会向配置中配置的lookupd发起register请求,lookupd维护着各各节点的topic+channel的meta信息。消费端可以通过这个组件发现nsq集群中指定topic的nsqd列表 |
nsqadmin | HTTP端口:4171 | 网页界面,用于管理nsq,可以看到nsq的统计数据,包括队列积压数据,也可以新增/删除/暂停/清空topic和channel |
3、nsq工具
nsq_stat | |
nsq_tail | Consumes the specified topic/channel and writes to stdout (in the spirit of tail(1)) |
nsq_to_file | Consumes the specified topic/channel and writes out to a newline delimited file, optionally rolling and/or compressing the file. |
nsq_to_http | 用于将指定topic下指定channel的消息,使用发送http请求的方式分发到指定url,更多详见 |
nsq_to_nsq | 用于将指定topic下指定channel的消息分发到指定nsqd的指定topic,更多详见 |
to_nsq | Takes a stdin stream and splits on newlines (default) for re-publishing to destination nsqd via TCP |
参考:https://nsq.io/components/utilities.html
4、nsq常用术语
参数 |
说明 |
---|---|
topic |
相当于消息队列名 |
channel |
相当于消息队列子队列名,一个topic可以有多个channel,每个channel都会消费每一条发给topic的消息
|
message |
当前分发(消费)中的消息数 |
图中,nsq上有一个叫”clicks“的topic,”clicks“下面有三条channel,其中channel名称为”metrics“的,有三个实例。消息A来到nsq后,被复制到三条channel,接着,在metrics上的那个A,被推送到了第二个实例上。接着,又来了一个叫B的消息,这一次,B被推送给了第一个实例进行处理。
主要有两个点:
1. 多个Consumer消费同一个Topic和同一个Channel:将会轮训,按序分配给就绪(当前无处理任务)的消费者。因此,多消费者情况下,无法保证有序执行。(像上面动图的"metrics"所示)
2. 多个Consumer消费同一个Topic和不同的Channel:将会将同一个数据同时到不同的Channel中。(像上面动图的三个Channel所示)
注意:Consumer与Topic没有直接联系,而是通过具体的Channel接受数据。如果Consumer退出,Channel不会自动删除。 如果不再需要,需要通过http端口删除Channel,否则很可能会导致磁盘空间不足。
6、nsqadmin管理
参数 |
说明 |
---|---|
Deferred |
Current count of messages that were requeued and explicitly deferred which are not yet available for delivery. 重新入队或明确要延迟分发而未分发的消息数 |
Depth |
Current sum of messages in memory + on disk (i.e. the “backlog” of messages pending delivery). 消息积压数,在一个topic下新建一个channel后,新推送给该topic的所有消息都会发送到该channel,如果没有消费或消费速度小于生产速度,会产生积压 |
In-Flight |
Current count of messages delivered but not yet finished ( 当前分发(消费)中的消息数(发送给消费端,但是消费端还没有回应的数目) |
Memory + Disk | 内存+磁盘消息积压数,nsq消息会优先放入内存中,当消息数超过mem-queue-size值(nsqd配置,一般建议此值不要设太大,以防内存使用超标)后,消息会写入磁盘 |
Ready Count |
Max number of messages that can be in-flight on this connection. This is controlled by a client’s 当前连接最大并发消费数据,值为0时不会消费新的数据,最大值由消费配置的max_in_flight决定,一般对于nsq集群而言,消费使用nsqlookupd地址,max_in_flight数会被均匀地分到每个与nsqd建立的连接上,例如nsq集群有2个nsqd,与每个nsqd都建立一个连接,max_in_flight为20,则每个连接的最大Ready Count为10 |
Requeued | Total times a message has been added back to the queue due to time outs or explicit requeues. |
Timed Out |
Total count of messages that were requeued after not receiving a response from the client before the configured timeout. 已重入队列但按配置的超时时间内还收到响应的消息数 |
Messages |
Total count of new messages recieved since node startup. |
Connections | Current number of connected clients. |
每条消息有3种结果:消费成功(FIN)、消费失败(REQ)、消费超时(TIMEOUT)。若nsqd进程意外退出,则内存中以及还没来得及刷入硬盘的消息都会丢失。
若消息在msg_timeout内没消费,则会自动重新入队列REQ;若消息在max_msg_timeout内还没消费,则会记为TIMEOUT
参考:https://nsq.io/components/nsqadmin.html
二、nsq的使用
1、nsq部署
1)单点部署
下面的示意图清晰的展示了整个NSQ架构的端口关系:
1. Consumer1首先使用HTTP连接nsqlookupd的4161端口,获取Topic1相关nsqd的TCP4150端口。
2. 使用TCP连接到nsqd的4150端口,并生成对应的Channel1;
特别需要注意的是,当所需Topic不变的情况下,就算nsqlookupd和nsqadmin进程都杀掉,也不影响nsqd的生产和消费。
2)集群
1. 当开启多个nsqd才存在集群的意义。
2. 尽量避免多个nsqd存在相同的Topic。如果多个nsqd真的存在相同的Topic的情况下,通过nsqlookupd将会返回所有这个Topic的IP并都能进行读取处理。
2、nsq的监控
监控工具:Prometheus
监控指标:
- 队列积压
3、nsq在QTT的实践
sdk: https://git.qutoutiao.net/gopher/nsqsdk
基于go-nsq封装的NSQ库,支持:
- 自动化集成trace
- 生产端自动负载均衡
- 消息高可靠
- 事务消息(事务消息暂不支持自动trace)
- 简单方便的扩缩容
三、nsq原理解读
1、nsq架构
2、nsq源码结构
nsq流程图:
3、nsq中的消息流转
messagePump:
messagePump selects over the in-memory and backend queue and writes messages to every channel for this topic
(监听 message 的更新的一些状态,以及时将消息持久化,同时写入到此 topic 对应的channel)
queueScanLoop:
负责处理延迟消息
生产者发布一个消息之后,通过以下途径保证消息可靠性:
- client连接上nsqd,并通过RDY参数告知可以处理的message数量
- nsqd 把消息暂存起来或者推送到各个连接的channels,对于状态为REQ 和 timeout的都会被暂存起来
- 客户端消费消息之后,回复nsqd一个FIN表示消息成功处理,或者REQ表示表示这个消息将会继续重新加入到队列中, 如果处理时间超过配置的timeout时间,则nsqd会把这个消息当成超时处理
- 如果客户端一直没有回复直到超时,则这个消息会被当成超时消息重新加入到队列中
4、消息传输的可靠性和持久化
在NSQ中,使用inflight机制来保证NSQ中消息”at least once”(至少被消费一次)。
在消息发送给Client之后,会将消息以及消息的timeout时间存储到优先级队列中pqueue。
如果客户端收到该消息,可以使用如下三个命令对此进行回复:
- FIN: Finish a message,表示成功处理完成。
- REQ: Re-queue a message,表示消息处理失败,需要重新入队再次进行处理。
- TOUCH: Reset the timeout for an in-flight message,表示需要重置消息的timeout时间。
如果客户端没有收到消息或是收到消息后没有进行任何的回复,则随着到达消息的超时时间,NSQD会将超时的消息重新入队,再次发送给客户端。
NSQD只能保证消息的”at least once”,至于消息的”exactly once”则需要业务端配合来实现。例如通过messageID来判断消息是否被消费过。
5、TCP 协议规范
参考:
https://nsq.io/clients/tcp_protocol_spec.html
以下为nsqd与nsqlookupd的通信:
四、nsq使用注意事项 && 思考
使用注意事项:
1、消息处理失败或超时(可配置超时时间)时会重新入队(由配置的消息最大重试次数决定),所以要保证代码的幂等性,且消费处理时间不宜太久,如果消费处理时间超过设置的超时时间且没有办法缩短,建议采用异步处理的方式,避免消息因处理超时而再次入队。(参考:https://nsq.io/overview/design.html#message-delivery-guarantees)
参考:
https://toutiao.io/posts/dboi02/preview
https://www.cnblogs.com/li-peng/p/11949258.html
https://blog.wongxinjie.com/2018/09/07/NSQ%E7%AC%94%E8%AE%B0/