ZeroMQ 4.3.5 部署指南

ZeroMQ(libzmq)4.3.5 部署指南:单机库安装、zmq_proxy broker 源码部署与 systemd 托管、Docker Compose 三服务编排(中转+发布+订阅)三种形态 0 到 1 全流程,含连通验证、运维动作与常见问题,全部命令逐条实测(2026-10-20)。

ZeroMQZeroMQ2026/10/20

ZeroMQ 4.3.5 部署指南

目标:把 ZeroMQ(libzmq 4.3.5)从「一个库」部署为「可用的消息基础设施」,覆盖三种落地形态——单机库安装、单机中转(zmq_proxy broker + systemd 托管)、Docker Compose 三服务编排(中转 + 发布 + 订阅),每种形态均给出完整部署步骤与逐命令实测回显。全部实测在 Ubuntu 22.04.5 LTS (x86_64) 完成,实测日期 2026-10-20。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(jammy),x86_64(cat /etc/os-release 实测:PRETTY_NAME="Ubuntu 22.04.5 LTS")
  • 组件版本:libzmq 4.3.5(官方最新稳定版,2023-10-09 发布,Releases Atom 源实查,查询日期 2026-10-20)
  • 编译工具链:gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0、GNU Make 4.3
  • Compose 形态运行时:python:3.12-slim + pyzmq 27.2.0(wheel 内置 libzmq 4.3.5,与基准版本一致,实测确认见 6.1 节)
  • 编排网络:自定义 bridge 10.89.58.0/24(网段选取避开宿主机已占用网段)
  • 宿主端口:8145(XSUB/发布侧)、8146(XPUB/订阅侧),均为空闲端口实测占用
  • 源码包:zeromq-4.3.5.tar.gz(2.5 MB,sha256 6653ef5910f17954861fe72332e68b03ca6e4d9c7160eb3a8de5a5a913bfab43,实测)

二、部署形态与方案选型

ZeroMQ 是消息库而非消息服务器:make install 之后系统里只有 libzmq.so 这一个共享库,没有守护进程、没有配置文件、没有管理端口。「部署 ZeroMQ」要解决的是两件事:① 把库装好;② 把使用这个库的应用进程跑起来。当应用需要「多对多」的消息交换时,再补一个极简中转进程(zmq_proxy broker)。

三种部署形态对照:

形态组成进程数适用场景
单机直连libzmq 库 + 应用进程仅应用自身进程内/进程间通信(inproc/ipc),端点直连
单机中转libzmq 库 + broker(zmq_proxy)+ systemd 托管 + 应用进程应用 N 个 + broker 1 个需要中心交换点的发布订阅(XPUB/XSUB)或请求应答中转
Compose 编排编排文件 + 三个服务(broker/pub/sub)3 个服务实例快速搭建可迁移的演示/开发/边缘环境,与宿主机解耦

选型建议:纯进程间通信选单机直连;生产级中心交换点选单机中转(进程模型最简、systemd 托管成熟);需要环境隔离与一键迁移(演示、开发联调、边缘盒子)选 Compose 编排。本文第四、五节展开单机两条线,第六节展开 Compose 编排(该节为容器化部署形态,行文按编排语境描述)。

三、前置条件

  • 一台 Ubuntu 22.04 服务器,root 或 sudo 权限;
  • 方案所需基础软件:编译线需要 build-essential、pkg-config、wget;Compose 线需要已安装 Docker 与 Docker Compose 插件(docker compose version 可确认);
  • 网络可达 GitHub Releases(下载源码包约 2.5 MB;受限网络请配置 http_proxy/https_proxy 后重试,实测直连失败经代理下载成功);
  • 端口规划:broker 默认占用 5555/5556(容器内)并映射到宿主 8145/8146,部署前确认端口未被占用:ss -ltn | grep -E '5555|5556|8145|8146' 应无输出。

四、单机部署形态:编译安装 libzmq 4.3.5(0→1)

本节把库从零装好,是后面两种形态的共同底座。完整编译细节与排障实录另见《Ubuntu 22.04 安装 ZeroMQ 4.3.5 指南》,此处保留完整可执行主线。

4.1 安装编译依赖

apt-get update
apt-get install -y build-essential wget pkg-config

实测回显(输出节选,实测末尾):

Processing triggers for ca-certificates (20260601~22.04.1) ...
Updating certificates in /etc/ssl/certs...
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.

4.2 下载源码包并校验

cd /usr/local/src
wget https://github.com/zeromq/libzmq/releases/download/v4.3.5/zeromq-4.3.5.tar.gz
sha256sum zeromq-4.3.5.tar.gz

实测回显:

6653ef5910f17954861fe72332e68b03ca6e4d9c7160eb3a8de5a5a913bfab43  zeromq-4.3.5.tar.gz

4.3 配置、编译、安装

tar xf zeromq-4.3.5.tar.gz
cd zeromq-4.3.5
./configure --prefix=/usr/local
make -j$(nproc)
make install
ldconfig

实测回显(configure 关键行节选):

configure: Using 'epoll' I/O thread polling system with CLOEXEC
configure: Using built-in SHA1
configure: Using mtree implementation to manage subscriptions
configure: Building stable and legacy API (no draft API)
config.status: creating src/platform.hpp
config.status: creating src/libzmq.pc

💡 说明:默认构建使用 epoll I/O 多路复用与内置 tweetnacl 密码实现;若需标准 libsodium 支持 CURVE 加密,改为 ./configure --prefix=/usr/local --with-libsodium(需先 apt-get install libsodium-dev,且必须在全新解压目录执行,详见《Ubuntu 22.04 安装 ZeroMQ 4.3.5 指南》5.7 节与 FAQ)。

4.4 安装验证

pkg-config --modversion libzmq
ls -l /usr/local/lib/libzmq.so.5*
python3 -c "import zmq; print(zmq.zmq_version())"

实测回显:

4.3.5
lrwxrwxrwx 1 root root       15 Oct 19 18:10 /usr/local/lib/libzmq.so.5 -> libzmq.so.5.2.5
-rwxr-xr-x 1 root root 14150720 Oct 19 18:10 /usr/local/lib/libzmq.so.5.2.5
4.3.5

验证通过:pkg-config 元数据、动态库文件、Python 运行时加载版本均为 4.3.5(Python 验证依赖 python3-zmq 包,如未安装可执行 apt-get install -y python3-zmq,其运行时会自动优先加载 /usr/local/lib 下的 4.3.5)。

五、单机中转部署:zmq_proxy broker + systemd 托管

需要「多发布者 → 多订阅者」的中心交换点时,用官方 zmq_proxy() 构件写一个约 30 行的中转程序:前端 XSUB 收发布者连接(5555),后端 XPUB 供订阅者连接(5556),进程内无限转发。本节将其编译、托管为 systemd 服务并做连通性与运维动作实测。

5.1 部署 broker 程序

写入源码并编译安装到 /usr/local/bin:

mkdir -p /opt/zmq-proxy
cat > /opt/zmq-proxy/broker.c <<'EOF'
#include <zmq.h>
#include <stdio.h>

int main(void)
{
    void *ctx = zmq_ctx_new();
    void *xsub = zmq_socket(ctx, ZMQ_XSUB);
    void *xpub = zmq_socket(ctx, ZMQ_XPUB);
    if (zmq_bind(xsub, "tcp://0.0.0.0:5555") != 0) {
        fprintf(stderr, "bind XSUB 5555 failed: %s\n", zmq_strerror(zmq_errno()));
        return 1;
    }
    if (zmq_bind(xpub, "tcp://0.0.0.0:5556") != 0) {
        fprintf(stderr, "bind XPUB 5556 failed: %s\n", zmq_strerror(zmq_errno()));
        return 1;
    }
    printf("zmq-proxy: XSUB tcp://0.0.0.0:5555  XPUB tcp://0.0.0.0:5556\n");
    fflush(stdout);
    zmq_proxy(xsub, xpub, NULL);
    zmq_close(xsub);
    zmq_close(xpub);
    zmq_ctx_term(ctx);
    return 0;
}
EOF
gcc -O2 -o /usr/local/bin/zmq-proxy /opt/zmq-proxy/broker.c $(pkg-config --cflags --libs libzmq)
ldd /usr/local/bin/zmq-proxy | grep zmq

实测回显:

        libzmq.so.5 => /usr/local/lib/libzmq.so.5 (0x000070a81bb5c000)

ldd 确认 broker 链接的正是 4.4 节安装的 /usr/local/lib/libzmq.so.5。

5.2 systemd 服务托管

写入单元文件 /etc/systemd/system/zeromq-proxy.service:

[Unit]
Description=ZeroMQ proxy broker (XSUB 5555 / XPUB 5556)
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/zmq-proxy
Restart=on-failure
RestartSec=3
User=nobody

[Install]
WantedBy=multi-user.target

💡 技术拓展:进程托管方式对比——

方式特点适用场景
systemd 服务原生托管、开机自启、崩溃自动拉起、journald 日志服务器常驻进程(本文采用)
supervisor配置简单、跨发行版一致已有 supervisor 体系的环境
nohup/后台脚本零依赖临时验证

本文因目标是生产常驻且宿主为标准 Ubuntu,采用 systemd。

启用并启动服务:

systemctl daemon-reload
systemctl enable zeromq-proxy
systemctl start zeromq-proxy
systemctl is-active zeromq-proxy
ss -ltn | grep -E ':5555|:5556'

实测回显:

Created symlink /etc/systemd/system/multi-user.target.wants/zeromq-proxy.service → /etc/systemd/system/zeromq-proxy.service.
active
LISTEN 0      100          0.0.0.0:5555      0.0.0.0:*
LISTEN 0      100          0.0.0.0:5556      0.0.0.0:*

验证通过:服务 active,5555(XSUB)与 5556(XPUB)双双进入 LISTEN。查看服务日志确认启动横幅:

journalctl -u zeromq-proxy --no-pager | tail -3

实测回显:

Oct 19 18:12:28 e9eb393be32c systemd[1]: /etc/systemd/system/zeromq-proxy.service:10: Special user nobody configured, this is not safe!
Oct 19 18:12:28 e9eb393be32c systemd[1]: Started ZeroMQ proxy broker (XSUB 5555 / XPUB 5556).
Oct 19 18:12:28 e9eb393be32c zmq-proxy[9148]: zmq-proxy: XSUB tcp://0.0.0.0:5555  XPUB tcp://0.0.0.0:5556

⚠️ 注意:第一行是 systemd 对 User=nobody 的安全告警(实测原文)。生产环境建议创建专用系统用户运行 broker:useradd -r -s /usr/sbin/nologin zmqproxy,并把单元中 User=nobody 改为 User=zmqproxy 后 systemctl daemon-reload && systemctl restart zeromq-proxy。

5.3 客户端连通验证

用 Python 绑定(pyzmq)做一次「发布 → 中转 → 订阅」全链路验证:

# broker_client_test.py —— 经 broker 的 PUB/SUB 全链路自测
import time
import zmq

ctx = zmq.Context.instance()
pub = ctx.socket(zmq.PUB)
pub.connect("tcp://127.0.0.1:5555")     # 发布者连 broker 的 XSUB 前端
sub = ctx.socket(zmq.SUB)
sub.connect("tcp://127.0.0.1:5556")     # 订阅者连 broker 的 XPUB 后端
sub.setsockopt_string(zmq.SUBSCRIBE, "")
time.sleep(0.5)                          # 等待订阅关系经 broker 建立
for i in range(3):
    pub.send_string("sys %d" % i)
    time.sleep(0.1)
time.sleep(0.2)
print([sub.recv_string() for _ in range(3)])

实测回显:

['sys 0', 'sys 1', 'sys 2']

验证通过:发布者与订阅者互不直连,消息经 broker 完成交换。

5.4 日常运维动作(实测)

systemctl restart zeromq-proxy
systemctl is-active zeromq-proxy
systemctl stop zeromq-proxy
systemctl is-active zeromq-proxy
ss -ltn | grep ':5555'

实测回显:

active
inactive

restart 后回到 active(ZeroMQ 端点会自动重连 broker,业务代码无需改动);stop 后状态 inactive,且 5555 端口监听消失(ss | grep 无输出)。如需恢复服务再执行 systemctl start zeromq-proxy。

六、Docker Compose 编排部署形态

本节用 Docker Compose 把「中转 + 发布 + 订阅」编排为三个服务,形成一套可整体迁移的环境。镜像层面 libzmq 官方没有发布任何 Docker 镜像,因此采用通用运行时镜像 + 语言绑定:python:3.12-slim 安装 pyzmq 绑定——pyzmq 27.2.0 的 wheel 内置 libzmq 4.3.5,与本文基准版本严格一致(下一节实测核对)。

6.1 镜像选型说明与版本核对

先确认 pyzmq 最新稳定版及其内置 libzmq 版本:

docker run --rm python:3.12-slim sh -c 'pip install -q pyzmq==27.2.0; python -c "import zmq; print(\"libzmq:\", zmq.zmq_version(), \" pyzmq:\", zmq.pyzmq_version())"'

实测回显:

libzmq: 4.3.5  pyzmq: 27.2.0

镜像选型结论:以 python:3.12-slim 为基座、pip install pyzmq==27.2.0 固定绑定版本,运行时 libzmq 即 4.3.5,无需单独编译安装库文件。

6.2 部署步骤

项目目录结构:

/opt/data/quzuo/tmp/zeromq/compose/
├── Dockerfile
├── docker-compose.yml
└── app/
    ├── broker.py   # 中转服务:zmq.proxy(XSUB, XPUB)
    ├── pub.py      # 发布服务:每秒发一条 hello N
    └── sub.py      # 订阅服务:打印收到的消息

Dockerfile:

FROM python:3.12-slim
RUN pip install --no-cache-dir pyzmq==27.2.0
WORKDIR /app
COPY app/broker.py app/pub.py app/sub.py /app/

app/broker.py:

import zmq


def main():
    ctx = zmq.Context.instance()
    xsub = ctx.socket(zmq.XSUB)
    xsub.bind("tcp://0.0.0.0:5555")
    xpub = ctx.socket(zmq.XPUB)
    xpub.bind("tcp://0.0.0.0:5556")
    print("broker: XSUB tcp://0.0.0.0:5555  XPUB tcp://0.0.0.0:5556", flush=True)
    zmq.proxy(xsub, xpub)


if __name__ == "__main__":
    main()

app/pub.py:

import os
import time

import zmq

BROKER = os.environ.get("ZMQ_BROKER", "zmq-proxy")
INTERVAL = float(os.environ.get("ZMQ_INTERVAL", "1"))


def main():
    ctx = zmq.Context.instance()
    pub = ctx.socket(zmq.PUB)
    pub.connect("tcp://%s:5555" % BROKER)
    n = 0
    while True:
        pub.send_string("hello %d" % n)
        n += 1
        time.sleep(INTERVAL)


if __name__ == "__main__":
    main()

app/sub.py:

import os

import zmq

BROKER = os.environ.get("ZMQ_BROKER", "zmq-proxy")


def main():
    ctx = zmq.Context.instance()
    sub = ctx.socket(zmq.SUB)
    sub.connect("tcp://%s:5556" % BROKER)
    sub.setsockopt_string(zmq.SUBSCRIBE, "")
    print("sub connected, waiting...", flush=True)
    while True:
        print("recv:", sub.recv_string(), flush=True)


if __name__ == "__main__":
    main()

docker-compose.yml(显式指定独立网段,避免与宿主上其他编排项目的网段冲突;container_name 命名遵循「产品-版本-用途」):

services:
  zmq-proxy:
    build: .
    container_name: zmq-4-3-5-proxy
    hostname: zmq-proxy
    command: ["python", "/app/broker.py"]
    ports:
      - "8145:5555"
      - "8146:5556"
    networks:
      - zmq-net
    restart: unless-stopped

  zmq-pub:
    build: .
    container_name: zmq-4-3-5-pub
    hostname: zmq-pub
    command: ["python", "/app/pub.py"]
    environment:
      ZMQ_BROKER: zmq-proxy
      ZMQ_INTERVAL: "1"
    depends_on:
      - zmq-proxy
    networks:
      - zmq-net
    restart: unless-stopped

  zmq-sub:
    build: .
    container_name: zmq-4-3-5-sub
    hostname: zmq-sub
    command: ["python", "/app/sub.py"]
    environment:
      ZMQ_BROKER: zmq-proxy
    depends_on:
      - zmq-proxy
    networks:
      - zmq-net
    restart: unless-stopped

networks:
  zmq-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.58.0/24

💡 说明:zmq-pub/zmq-sub 通过服务名 zmq-proxy 直连 broker——Compose 网络内置 DNS 按服务名/主机名解析,broker 的服务名同时是它的稳定地址;三个服务同属 zmq-net 自定义网络。网段 10.89.58.0/24 为部署前核查宿主已占用网段后选取的空闲槽位。

构建并启动(首次构建需拉取 python:3.12-slim 并安装 pyzmq,约 1 分钟):

docker compose -p zeromq up -d --build

实测回显(输出节选,实测末尾):

 Image compose-zmq-proxy Built
 Image compose-zmq-pub Built
 Image compose-zmq-sub Built
 Container zmq-4-3-5-proxy Started
 Container zmq-4-3-5-pub Started
 Container zmq-4-3-5-sub Started

6.3 部署验证

服务状态:

docker compose -p zeromq ps --format 'table {{.Name}}\t{{.Status}}'

实测回显:

NAME              STATUS
zmq-4-3-5-proxy   Up 8 seconds
zmq-4-3-5-pub     Up 8 seconds
zmq-4-3-5-sub     Up 8 seconds

订阅端日志(消息已从 pub 经 broker 流转到 sub):

docker compose -p zeromq logs zmq-sub | tail -6

实测回显:

zmq-4-3-5-sub  | recv: hello 3
zmq-4-3-5-sub  | recv: hello 4
zmq-4-3-5-sub  | recv: hello 5
zmq-4-3-5-sub  | recv: hello 6
zmq-4-3-5-sub  | recv: hello 7
zmq-4-3-5-sub  | recv: hello 8

broker 启动横幅:

docker compose -p zeromq logs zmq-proxy | head -3

实测回显:

zmq-4-3-5-proxy  | broker: XSUB tcp://0.0.0.0:5555  XPUB tcp://0.0.0.0:5556

宿主端口监听与网络拓扑:

ss -ltn | grep -E '8145|8146'
docker network inspect zeromq_zmq-net --format '{{(index .IPAM.Config 0).Subnet}}'
docker ps --filter name=zmq-4-3-5 --format '{{.Names}}' | while read c; do docker inspect $c --format '{{.Name}} {{range $k,$v := .NetworkSettings.Networks}}{{$k}}={{$v.IPAddress}} {{end}}'; done

实测回显:

LISTEN 0      4096         0.0.0.0:8145       0.0.0.0:*
LISTEN 0      4096         0.0.0.0:8146       0.0.0.0:*
LISTEN 0      4096            [::]:8145          [::]:*
LISTEN 0      4096            [::]:8146          [::]:*
10.89.58.0/24
/zmq-4-3-5-proxy zeromq_zmq-net=10.89.58.2
/zmq-4-3-5-pub zeromq_zmq-net=10.89.58.3
/zmq-4-3-5-sub zeromq_zmq-net=10.89.58.4

验证通过:宿主 8145/8146 映射 broker 的 XSUB/XPUB;三个服务位于 10.89.58.0/24 自定义网络(.2/.3/.4),发布-中转-订阅全链路有真实消息流转。

6.4 环境变量表

Compose 服务可调环境变量(由 6.2 节应用脚本定义,均有默认值):

环境变量所属服务默认值说明
ZMQ_BROKERzmq-pub / zmq-subzmq-proxybroker 服务地址(Compose 网络内用服务名;跨机部署改填 broker 所在主机 IP/域名)
ZMQ_INTERVALzmq-pub1发布间隔(秒),调小可增大消息速率

libzmq 本身无运行时环境变量,其行为由 socket 选项控制,见第七节。

6.5 数据持久化说明

ZeroMQ 是纯内存消息库:broker 只做转发、不落盘、无存储目录,三个服务均无状态,不需要数据卷。唯一值得持久化的是日志(默认走 Docker 日志驱动,docker compose logs 即可读取);若需文件日志,为 broker 服务追加:

    volumes:
      - ./logs:/app/logs

⚠️ 注意:不要把「持久化消息」的预期带入本形态——broker 进程重启期间未投递的 PUB 消息即丢失,这是 ZeroMQ 的设计语义(见 7.1 节 LINGER/HWM)。

6.6 等价 docker run 命令

不使用编排文件时,等价的裸命令序列(网络、构建、三服务逐个拉起):

docker network create --driver bridge --subnet 10.89.58.0/24 zeromq-net
docker build -t zeromq-app:4.3.5 .
docker run -d --name zmq-4-3-5-proxy --hostname zmq-proxy --network zeromq-net -p 8145:5555 -p 8146:5556 zeromq-app:4.3.5 python /app/broker.py
docker run -d --name zmq-4-3-5-pub --hostname zmq-pub --network zeromq-net -e ZMQ_BROKER=zmq-proxy -e ZMQ_INTERVAL=1 zeromq-app:4.3.5 python /app/pub.py
docker run -d --name zmq-4-3-5-sub --hostname zmq-sub --network zeromq-net -e ZMQ_BROKER=zmq-proxy zeromq-app:4.3.5 python /app/sub.py

6.7 常用运维命令

docker compose -p zeromq ps                        # 查看服务状态
docker compose -p zeromq logs -f zmq-sub           # 跟踪订阅端消息流
docker compose -p zeromq restart zmq-proxy         # 重启 broker(pub/sub 自动重连)
docker compose -p zeromq stop                      # 停止全部服务(保留容器)
docker compose -p zeromq up -d                     # 再次拉起
docker compose -p zeromq down                      # 删除容器与网络(保留镜像)
docker compose -p zeromq up -d --build             # 改代码后重建镜像并滚动更新

其中 restart zmq-proxy 演示 broker 重启后的自愈能力:ZeroMQ 端点按 ZMQ_RECONNECT_IVL(默认 100ms,见 7.1 节)自动重连,pub/sub 无需任何操作即恢复收发。

七、配置参数说明

ZeroMQ 没有 broker 配置文件,一切「配置」都发生在两个层面:socket 选项(代码内设置)与端点 URL(bind/connect 参数)。部署视角最常用的 socket 选项如下(默认值逐一取自 libzmq 4.3.5 源码 src/options.cpp 实查):

7.1 常用 socket 选项

选项默认值说明
ZMQ_SNDHWM / ZMQ_RCVHWM1000 / 1000发/收方向高水位:队列积压超过阈值后,PUB/SUB 类模式开始丢弃消息,REQ/REP 类模式阻塞发送
ZMQ_LINGER-1套接字关闭后未发完消息的滞留时间(毫秒);-1 表示无限等待,0 表示立即丢弃,建议显式设为有限值(如 1000)
ZMQ_RECONNECT_IVL100断线重连基础间隔(毫秒),broker 重启后端点约 100ms 内自动重连
ZMQ_RECONNECT_IVL_MAX0重连最大间隔(毫秒),0 表示退避间隔不增长
ZMQ_MAXMSGSIZE-1单条消息大小上限,-1 表示不限制;暴露公网时建议设置
ZMQ_IMMEDIATE01 = 连接未完成前不允许消息排队,避免首批发消息「黑洞」
ZMQ_TCP_KEEPALIVE-1TCP keepalive 开关,-1 表示沿用系统默认
ZMQ_SUBSCRIBE / ZMQ_UNSUBSCRIBE无SUB 套接字专用:按消息前缀订阅/退订主题

💡 技术拓展:消息积压策略不止「丢」一种,ZeroMQ 按 socket 类型在 HWM 触发后的行为不同——PUB/SUB、PUSH/PULL 采用「丢新消息」(partially:到达阈值后拒绝新消息),REQ/REP、DEALER 采用「阻塞发送」。高吞吐广播场景要接受丢弃语义,可靠投递场景需要业务层加确认机制或改用支持持久化的中间件。

7.2 端点 URL 与绑定约定

端点示例用途
tcp 通配绑定tcp://*:5555broker/服务端监听所有网卡
tcp 定向连接tcp://zmq-proxy:5555客户端连接服务端(Compose 内服务名 / 生产内网主机名 / IP)
ipcipc:///tmp/zmq-demo.ipc同机进程间通信(自动创建 socket 文件,注意清理与权限)
inprocinproc://pipeline同进程线程间通信(需共享同一 Context)

八、常见问题(FAQ)

Q1:docker compose up 报 invalid pool request: Pool overlaps with other one on this address space? 实测踩坑:compose 文件指定的 10.89.57.0/24 与宿主上其他编排项目的网络重叠,网络创建失败且首个服务卡在 Creating。解决:先用 docker network inspect <网络名> --format '{{(index .IPAM.Config 0).Subnet}}' 逐一盘点已占用网段,选一个空闲槽位写入 compose 的 ipam.config.subnet 后重新 up(本文由 57 段改用 58 段后成功)。

Q2:compose 项目与其他项目的容器互相干扰(up 时出现 orphan 警告)? 在同名目录运行 compose 会默认生成同名项目(目录名即项目名),与其他项目撞名导致 Found orphan containers 警告与资源混淆。解决:所有 compose 命令显式带独立项目名 -p zeromq(本文所有命令已如此)。

Q3:订阅端一条消息都收不到? 最常见原因是「慢加入者」:PUB 端先启动发送、SUB 端后建立订阅关系,早期消息被丢弃。解决:订阅端先 setsockopt_string(zmq.SUBSCRIBE, ...)、connect 后留出几百毫秒(本文脚本 time.sleep(0.5))再让发布端开跑;生产上建议用 XPUB 的订阅事件或业务层握手对齐。

Q4:broker 端口绑定失败(Address already in use)? 用 ss -ltnp | grep -E '5555|5556' 找到占用进程;本部署约定 5555/5556 仅在编排网络内部使用、宿主侧统一映射 8145/8146,若宿主侧端口冲突,改 compose 端口映射左侧为其他空闲端口(如 8147/8148)并同步防火墙策略。

Q5:systemd 日志出现 Special user nobody configured, this is not safe!? 这是对 User=nobody 的常规安全提示(5.2 节实测原文),服务仍正常运行。生产环境按 5.2 节建议改为专用系统用户即可消除。

Q6:broker 重启后消息会丢吗? 会。ZeroMQ 全内存、无持久化,PUB 模式下 broker 重启窗口内发布的消息不会补投(PUB 本身也是「丢弃语义」)。要求不丢消息时:改用 REQ/REP + 业务层重试,或换用支持持久化的消息中间件。

九、参考链接