NSQ 1.3.0 集群部署指南
NSQ 1.3.0 集群部署指南:3 台 Ubuntu 22.04 实测 1 nsqlookupd + 2 nsqd + nsqadmin 拓扑,覆盖服务发现、跨节点消息分发、channel 负载均衡、节点故障演练与官方口径的副本策略边界说明。
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/TCP | nsq1 | nsqlookupd:nsqd 注册心跳 |
| 4161/HTTP | nsq1 | nsqlookupd:发现查询(nsqadmin/客户端消费) |
| 4171/HTTP | nsq1 | nsqadmin:Web 管理台 |
| 4150/TCP、4151/HTTP | nsq2、nsq3 | nsqd:生产/消费与 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) |
由此得出的运维红线:
- nsqd 节点磁盘损坏 = 该节点上未消费消息丢失(内存队列部分更是在进程退出即消失;
-mem-queue-size=0可强制全落盘,把丢失面缩小到磁盘本身); - 需要消息级冗余时,官方路径是运行多个接收同一消息流的独立集群,或用官方工具 nsq_to_nsq/nsq_to_file 做旁路归档;
- 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 实测)。
八、参考链接
- 官方部署文档(Topology Patterns):https://nsq.io/deployment/topology_patterns.html
- 官方组件文档:https://nsq.io/components/nsqd.html 、https://nsq.io/components/nsqlookupd.html 、https://nsq.io/components/nsqadmin.html
- 官方投递保证:https://nsq.io/overview/features_and_guarantees.html
- 站内相关:《NSQ 1.3.0 介绍与选型指南》、《Ubuntu 22.04 安装 NSQ 1.3.0 指南》、《NSQ 1.3.0 常用 CLI 命令与工具详解》