NSQ 1.3.0 集群部署指南

NSQ 1.3.0 集群部署指南:3 台 Ubuntu 22.04 实测 1 nsqlookupd + 2 nsqd + nsqadmin 拓扑,覆盖服务发现、跨节点消息分发、channel 负载均衡、节点故障演练与官方口径的副本策略边界说明。

NSQ消息队列NSQ2026/10/20

NSQ 1.3.0 集群部署指南

目标:在 3 台 Ubuntu 22.04 服务器上部署 NSQ 1.3.0 集群(1 个 nsqlookupd + 2 个 nsqd + 1 个 nsqadmin),实测服务发现、跨节点消息分发、channel 负载均衡与节点故障恢复,并明确 NSQ 的副本策略边界。以下全部命令与回显均为逐节点实测。

一、环境信息

项值
操作系统Ubuntu 22.04 LTS(x86_64,3 台同构)
NSQ 版本1.3.0(官方最新稳定版,2026-10-20 实查)
集群网段10.89.63.0/24
主机规划nsq1=10.89.63.2(nsqlookupd + nsqadmin)、nsq2=10.89.63.3(nsqd)、nsq3=10.89.63.4(nsqd)
安装方式官方二进制(安装步骤见《Ubuntu 22.04 安装 NSQ 1.3.0 指南》,三节点均需完成到「4.4 验证安装」)
数据目录/var/lib/nsq(nsqd 节点)
专用账号nsq(系统用户,三节点均需创建)

1.1 端口清单

端口归属节点用途
4160/TCPnsq1nsqlookupd:nsqd 注册心跳
4161/HTTPnsq1nsqlookupd:发现查询(nsqadmin/客户端消费)
4171/HTTPnsq1nsqadmin:Web 管理台
4150/TCP、4151/HTTPnsq2、nsq3nsqd:生产/消费与 HTTP 接口

二、集群架构与方案选型

2.1 实测拓扑

生产者 ──HTTP──► nsq2(nsqd) ──TCP 心跳──┐
                                        ├─► nsq1(nsqlookupd:4160/4161) ◄──查询── 消费者
生产者 ──HTTP──► nsq3(nsqd) ──TCP 心跳──┘
                     nsq1(nsqadmin:4171) ──聚合展示──► 浏览器
  • nsqd 是数据节点:消息只落在接收它的 nsqd,节点之间零耦合、互不复制;
  • nsqlookupd 是无状态发现节点:只维护「topic → nsqd 列表」的注册视图,不碰消息;
  • nsqadmin 是观察节点:从 lookupd 聚合统计,可执行清空/删除/暂停等管理动作。

2.2 拓扑选型说明

官方《Topology Patterns》口径:生产环境建议运行 3 个 nsqlookupd(互相独立、客户端同时配置 3 个地址轮询),nsqd 按 topic 流量横向加节点。本文采用 1 lookupd + 2 nsqd 的最小可验证拓扑,核心机制(注册、发现、分发)与 3 lookupd 完全一致,差异仅在发现服务的容错性(见第七章 FAQ Q3 与边界说明)。

三、前置条件(三节点逐台执行)

3.1 安装 NSQ 1.3.0

三台机器都按《Ubuntu 22.04 安装 NSQ 1.3.0 指南》方案 A 执行:下载 nsq-1.3.0.linux-amd64.go1.21.5.tar.gz → sha256 校验(eeeb62e003ca7c514b6cb6ef1bbe9d3754f14657ef276e3ebb54b5d57bd7988a)→ 安装到 /usr/local/nsq-1.3.0 → 软链 /usr/local/nsq → 配置 PATH。完成后在每台机器验证:

nsqd --version
# nsqd v1.3.0 (built w/go1.21.5)

3.2 创建运行账号与目录

useradd -r -s /usr/sbin/nologin nsq
mkdir -p /var/lib/nsq /etc/nsq
chown -R nsq:nsq /var/lib/nsq

3.3 主机名与hosts解析

三节点用主机名互通(nsqd 注册地址、lookupd 查询地址都写主机名)。/etc/hosts 三台一致:

10.89.63.2  nsq1
10.89.63.3  nsq2
10.89.63.4  nsq3

实测确认三节点互解析(在 nsq2 上执行):

getent hosts nsq1 nsq2 nsq3
# 10.89.63.2    nsq1
# 10.89.63.3    nsq2
# 10.89.63.4    nsq3

3.4 放行端口

节点间互访放行 4150、4151、4160、4161、4171(ufw 环境示例,在 nsq1 执行):

ufw allow 4160:4171/tcp

四、集群部署步骤(逐节点配置)

4.1 节点一 nsq1:部署 nsqlookupd + nsqadmin

创建 /etc/systemd/system/nsqlookupd.service:

[Unit]
Description=NSQ lookup daemon (nsqlookupd)
Documentation=https://nsq.io/components/nsqlookupd.html
After=network.target

[Service]
Type=simple
User=nsq
Group=nsq
ExecStart=/usr/local/nsq/bin/nsqlookupd -broadcast-address=nsq1
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

创建 /etc/systemd/system/nsqadmin.service:

[Unit]
Description=NSQ web admin (nsqadmin)
Documentation=https://nsq.io/components/nsqadmin.html
After=network.target

[Service]
Type=simple
User=nsq
Group=nsq
ExecStart=/usr/local/nsq/bin/nsqadmin -lookupd-http-address=nsq1:4161
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

逐个启用并启动:

systemctl daemon-reload
systemctl enable nsqlookupd
# Created symlink /etc/systemd/system/multi-user.target.wants/nsqlookupd.service → /etc/systemd/system/nsqlookupd.service.
systemctl enable nsqadmin
# Created symlink /etc/systemd/system/multi-user.target.wants/nsqadmin.service → /etc/systemd/system/nsqadmin.service.
systemctl start nsqlookupd
systemctl is-active nsqlookupd
# active
systemctl start nsqadmin
systemctl is-active nsqadmin
# active

4.2 节点二 nsq2:部署 nsqd

创建 /etc/systemd/system/nsqd.service:

[Unit]
Description=NSQ daemon (nsqd)
Documentation=https://nsq.io/components/nsqd.html
After=network.target

[Service]
Type=simple
User=nsq
Group=nsq
ExecStart=/usr/local/nsq/bin/nsqd -data-path=/var/lib/nsq -broadcast-address=nsq2 -lookupd-tcp-address=nsq1:4160
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

参数说明:-data-path 持久化磁盘队列;-broadcast-address=nsq2 是注册到 lookupd 的地址,消费端会按它回连,必须写集群内可达的主机名/IP;-lookupd-tcp-address=nsq1:4160 指向发现服务(生产用 3 lookupd 时,该参数可重复传三次)。

启用并启动:

systemctl daemon-reload
systemctl enable nsqd
# Created symlink /etc/systemd/system/multi-user.target.wants/nsqd.service → /etc/systemd/system/nsqd.service.
systemctl start nsqd
systemctl is-active nsqd
# active
journalctl -u nsqd --no-pager -n 4 -o cat

实测回显(节选):

[nsqd] INFO: nsqd v1.3.0 (built w/go1.21.5)
[nsqd] INFO: LOOKUPD: adding peer nsq1 (nsq1:4160)
[nsqd] INFO: TCP: listening on [::]:4150
[nsqd] INFO: HTTP: listening on [::]:4151

4.3 节点三 nsq3:部署 nsqd

与 nsq2 完全同构,仅 -broadcast-address 改为本机名。创建 /etc/systemd/system/nsqd.service:

[Unit]
Description=NSQ daemon (nsqd)
Documentation=https://nsq.io/components/nsqd.html
After=network.target

[Service]
Type=simple
User=nsq
Group=nsq
ExecStart=/usr/local/nsq/bin/nsqd -data-path=/var/lib/nsq -broadcast-address=nsq3 -lookupd-tcp-address=nsq1:4160
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable nsqd
systemctl start nsqd
systemctl is-active nsqd
# active

五、集群验证

5.1 服务发现:两台 nsqd 已注册

在 nsq1 查询 lookupd 节点视图:

curl -s http://nsq1:4161/nodes

实测回显(字段完整保留,端口随机段为实验环境内部端口号,真实服务器两台 nsqd 均为 4150/4151):

{"producers":[{"remote_addresses":["10.89.63.3:45454","10.89.63.4:51722"],"hostname":"nsq2","broadcast_address":"nsq2","tcp_port":4150,"http_port":4151,"version":"1.3.0","topics":[],"tombstones":[]},{"remote_addresses":["10.89.63.4:51726"],"hostname":"nsq3","broadcast_address":"nsq3","tcp_port":4150,"http_port":4151,"version":"1.3.0","topics":[],"tombstones":[]}]}

nsqadmin 同步可见(nsqadmin 的节点 API 即聚合 lookupd 数据):

curl -s http://nsq1:4171/api/nodes
# {"nodes":[{..."broadcast_address":"nsq2","tcp_port":4150,...},{..."broadcast_address":"nsq3","tcp_port":4150,...}]}

浏览器访问 http://10.89.63.2:4171/ 即为 nsqadmin 控制台(HOSTS 页会列出 nsq2/nsq3 两个数据节点)。

5.2 创建 topic 并跨节点发布

在 nsq2 上通过 HTTP 创建 topic(任意一台 nsqd 都可执行,创建动作只作用于本机):

curl -s -o /dev/null -w 'topic/create HTTP %{http_code}\n' -X POST 'http://nsq2:4151/topic/create?topic=orders'
# topic/create HTTP 200

向两台 nsqd 分别发布消息(HTTP 发布无需客户端库,消息体即请求体):

curl -s -d 'order-A1' 'http://nsq2:4151/pub?topic=orders'
# OK
curl -s -d 'order-A2' 'http://nsq2:4151/pub?topic=orders'
# OK
curl -s -d 'order-B1' 'http://nsq3:4151/pub?topic=orders'
# OK
curl -s -d 'order-B2' 'http://nsq3:4151/pub?topic=orders'
# OK

⚠️ 说明:order-B1/B2 直接 pub 到 nsq3 时,nsqd 会对不存在的 topic 自动创建并登记——NSQ 的 topic 创建是惰性的(官方 FAQ 口径)。此时 orders 同时存在于 nsq2 与 nsq3 两台数据节点上。

向 lookupd 查询 topic 的生产者分布(发现机制的核心接口):

curl -s 'http://nsq1:4161/lookup?topic=orders'

实测回显(节选):

{"channels":[],"meta":{"delivered":true},"producers":[{"broadcast_address":"nsq2","hostname":"nsq2","tcp_port":4150,"http_port":4151,"version":"1.3.0","tombstones":[],"topics":["orders"]},{"broadcast_address":"nsq3","hostname":"nsq3","tcp_port":4150,"http_port":4151,"version":"1.3.0","tombstones":[],"topics":["orders"]}]}

nsqadmin 聚合视图(/api/topics):

curl -s http://nsq1:4171/api/topics

实测回显(节选):{"topics":[{"topic_name":"orders","channels":[{"channel_name":"pay",...}],"nodes":["nsq2","nsq3"],"depth":6,"memory_depth":6,...}]}

5.3 消费端发现与跨节点汇聚

关键认知:channel 是跨节点的逻辑消费单元。经 lookupd 订阅的消费者会同时连上持有该 topic 的所有 nsqd,每个节点本地 channel 内做负载均衡,整体表现为「全量消息汇聚、每条只投一次」。

在 nsq3 上启动经 lookupd 订阅的消费者(官方工具 nsq_tail):

nsq_tail --topic=orders --channel=pay --lookupd-http-address=nsq1:4161

实测回显(节选,可看到同时连上两个数据节点):

2026/10/19 17:26:22 INF 1 [orders/pay] (nsq1:4161) querying nsqlookupd (http://nsq1:4161/lookup?topic=orders)
2026/10/19 17:26:22 INF 1 [orders/pay] querying nsqlookupd (http://nsq1:4161/lookup?topic=orders)
2026/10/19 17:26:22 INF 1 [orders/pay] (nsq3:4150) connecting to nsqd
2026/10/19 17:26:22 INF 1 [orders/pay] (nsq2:4150) connecting to nsqd
2026/10/19 17:26:23 orders-A2
2026/10/19 17:26:23 orders-A1
2026/10/19 17:26:23 orders-B1
2026/10/19 17:26:23 orders-B2

单消费者即收到两台 nsqd 上的全部消息——跨节点汇聚验证通过。

5.4 channel 内负载均衡

在 nsq2、nsq3 各启动一个同 channel(notify)的消费者,向 nsq2 集中发布 6 条消息:

nsq_tail --topic=orders --channel=notify --lookupd-http-address=nsq1:4161

实测分发结果(两台机器各自回显,节选):

nsq2 侧消费者:notify-msg-1  notify-msg-4  notify-msg-5
nsq3 侧消费者:notify-msg-2  notify-msg-3  notify-msg-6

6 条消息被两个消费者均分(3/3),没有任何一条被重复消费——同一 channel 内的负载均衡跨节点生效,验证通过。结合 5.3 可总结 NSQ 的两级投递模型:channel 之间广播、channel 内均衡。

5.5 消息状态核对(nsq_stat)

官方聚合统计工具(在任一节点执行):

nsq_stat -interval 1s -count 1 --lookupd-http-address=nsq1:4161 --topic=orders --channel=pay

实测回显:

Connections per channel, per nsqd node

depth    mem-depth  inflt    def    re-q    timeout    msgs    e2e
0        0          0        0      0       0          6       0.0ms
nsq3:4150 [client: 198.18.x.x]
total 1 connections

六、高可用与故障演练

6.1 演练:数据节点下线与恢复

下线 nsq3 的 nsqd(模拟单数据节点故障):

systemctl stop nsqd   # 在 nsq3 上执行

约 1 秒后 lookupd 视图即收缩(nsqd 心跳超时未续约即被摘除):

curl -s http://nsq1:4161/nodes | python3 -c 'import json,sys; print([p["broadcast_address"] for p in json.load(sys.stdin)["producers"]])'
# ['nsq2']

此时集群仍然可用:向存活的 nsq2 正常发布,消费者经 lookupd 只连 nsq2,消息照常收取。实测在 nsq2 起一个 failover channel 消费者后连发 3 条:

curl -s -d 'fail-1' 'http://nsq2:4151/pub?topic=orders' >/dev/null
curl -s -d 'fail-2' 'http://nsq2:4151/pub?topic=orders' >/dev/null
curl -s -d 'fail-3' 'http://nsq2:4151/pub?topic=orders' >/dev/null

消费者实测回显:

2026/10/19 17:33:31 fail-1
2026/10/19 17:33:32 fail-2
2026/10/19 17:33:33 fail-3

恢复 nsq3:

systemctl start nsqd   # 在 nsq3 上执行

nsqd 启动后主动重新注册,lookupd 视图自动扩回:

curl -s http://nsq1:4161/nodes | python3 -c 'import json,sys; print([p["broadcast_address"] for p in json.load(sys.stdin)["producers"]])'
# ['nsq2', 'nsq3']

故障期间落在 nsq3 本地磁盘/内存的未消费消息,恢复后仍留在 nsq3,继续被该 topic 的订阅者收走(NSQ 无副本,数据与节点同生共死,见 6.2)。

6.2 副本策略边界说明(必读)

官方《Features & Guarantees》原文:「There is no built in replication. The only thing you can do is run multiple clusters that are receiving the same messages via different nsqd hosts.」 NSQ 1.3.0 没有任何消息副本机制,这是它与 Kafka/NATS JetStream/RabbitMQ 仲裁队列最本质的差异:

策略模型NSQ 的对应
多副本同步Raft 多数派/半同步复制,RPO≈0❌ 不存在(Kafka ISR、NATS JetStream Raft、RabbitMQ 仲裁队列均属此类)
异步复制/镜像主从异步,可容忍少量丢失❌ 不存在
无副本+多节点分布+客户端故障转移数据只存接收节点,靠「多节点都挂着 + 客户端换节点重连」保可用性,不保数据冗余✅ NSQ 的设计(官方建议的做法:把消息流按业务拆到多个 nsqd,生产端对 nsqd 故障做 failover,如官方工具 nsq_writer)

由此得出的运维红线:

  1. nsqd 节点磁盘损坏 = 该节点上未消费消息丢失(内存队列部分更是在进程退出即消失;-mem-queue-size=0 可强制全落盘,把丢失面缩小到磁盘本身);
  2. 需要消息级冗余时,官方路径是运行多个接收同一消息流的独立集群,或用官方工具 nsq_to_nsq/nsq_to_file 做旁路归档;
  3. lookupd 多实例 ≠ 数据高可用:lookupd 只是无状态视图,多跑几个只提高「发现」的可用性。

💡 技术拓展:无副本系统获得可用性的另一种思路是「端侧冗余」——由生产者把同一消息双写/多写到互不感知的 nsqd 节点(类似 NATS Core「无持久化、靠客户端重发」的哲学),消费端按业务 ID 去重。NSQ 官方工具 nsq_writer 就是封装了「多 nsqd 地址 + 自动 failover」的生产端;但要清醒:双写只覆盖「节点宕机」,不覆盖「节点假死丢消息」。

6.3 高可用部署建议

  • 发现层:3 台机器各跑 1 个 nsqlookupd,客户端(go-nsq 等)与 nsqadmin 都配 3 个 -lookupd-http-address;任一 lookupd 宕机不影响收发。
  • 数据层:nsqd ≥ 3 台,按 topic 流量分布;生产端实现/使用 failover(发布失败即换节点)。
  • 归档层:低消息量的关键审计流可挂 nsq_to_file 常驻消费到本地文件,构成旁路存证。
  • 管理台:nsqadmin 单实例即可(它只是视图),宕机无业务影响。

七、常见问题(FAQ)

Q1:消费者连上 lookupd 后收不到新 topic 的消息? 实测复现:先启动经 lookupd 订阅的消费者、后创建 topic 时,消费者最初发现不到该 topic。机制:消费端默认每 60s 轮询 lookupd(go-nsq lookupd_poll_interval),topic 是惰性注册的。处理:生产端先建 topic 再上消费者,或调小客户端轮询间隔。

Q2:消费者报 E_NOT_FOUND / 连不上某台 nsqd? 典型原因是 nsqd 的 -broadcast-address 写错(如写了内网不可达地址或 127.0.0.1),lookupd 把它通告给了消费者。排查:curl http://nsq1:4161/nodes 看 broadcast_address 是否为消费端可达地址。

Q3:nsqlookupd 宕机会怎样? 实测结论:已建立的生产/消费连接完全不受影响(数据在 nsqd,收发不经过 lookupd);受影响的只有「新 topic 的注册/发现」与 nsqadmin 视图。生产上跑 3 个 lookupd 即可消除该影响。

Q4:topic/create 之后 pub 报 404? 两种传参习惯差异:/topic/create 只认 URL 查询参数;而 /pub 的 topic 也写查询参数即可。若用 POST body 传 topic,/topic/create 会报 MISSING_ARG_TOPIC(v1.3.0 实测:{"message":"MISSING_ARG_TOPIC"})。此外旧版教程里的 /empty、/pause 在 1.x 已改为 /topic/empty、/topic/pause、/channel/empty、/channel/pause(源码路由表实查),旧路径返回 {"message":"NOT_FOUND"}。

Q5:两台 nsqd 的消息会互相同步吗? 不会。NSQ 没有节点间数据同步(官方保证页原文 no built in replication),「orders 在 nsq2 与 nsq3 都有」只因为两个节点都收到过发布。消费端经 lookupd 会把两台的消息合并成一条流收取,这是发现机制带来的「逻辑汇聚」,不是数据复制。

Q6:如何把 nsqd 摘出维护窗口? 先用 nsqadmin 对该节点上的 channel 执行暂停(POST /channel/pause,源码路由表实查)或等存量消息消费完,再 systemctl stop nsqd;客户端会自动从 lookupd 视图中剔除该节点。恢复后 nsqd 自动重注册(见 6.1 实测)。

八、参考链接