Skip to content

ZeroMQ inproc 传输使用注意 ​

inproc:// 是 ZeroMQ 的进程内传输:消息在进程内存中传递,不经内核网络栈,常用于同进程内线程间的通信(例如 socket monitor 的事件通道就是一个 inproc PAIR)。它有两处容易踩坑的地方:端点命名有明确的唯一性规则,以及一个至今未修复的 socket 关闭 bug(libzmq #4778)。本文整理这两条注意事项,适合在代码里使用 inproc:// endpoint 的开发者。

端点命名规则 ​

依据官方文档 zmq_inproc(7):

  • endpoint 形如 inproc://name,name 是任意字符串,官方对格式不做其他限制;
  • name 在所属 context 内唯一——既不是进程内唯一(不同 context 可以重名),也不是跨进程全局唯一;对同一个 context 同名 bind 两次会得到 Address already in use;
  • 名字最长 256 字符;
  • 自 libzmq 4.0 起,connect 与 bind 的先后顺序不再重要(与 tcp 行为一致,connect 先于 bind 也可以)。

「在 context 内唯一」是最容易误解的一条:多个模块各自约定了 inproc://local 这类名字,只要它们共用同一个 zmq_context,后 bind 的就会失败。

socket 关闭影响新 socket 创建(libzmq #4778) ​

libzmq issue #4778(2026-10 查证仍为 open 状态)报告:inproc 模式下,已打开 socket 的关闭会继续影响新 socket 的创建。

issue 中的最小复现代码:

cpp
zmq::context_t ctx;
int count = 0;
while (true) {
    try {
        zmq::socket_t sock(ctx, zmq::socket_type::pull);
        sock.set(zmq::sockopt::linger, 0);
        sock.connect("inproc://szn");  // 没有对应的 bind 端点
        ++count;
    } catch (const std::exception &e) {
        // 第一轮:count 达 1023 时抛 "Too many open files"
        // sleep 5s 后重试:count 为 0——新 socket 立即创建失败
        count = 0;
        std::this_thread::sleep_for(5s);
    }
}

两个值得注意的观察:

  • 每个 zmq socket 内部有一个 signaler(Linux 上用 eventfd 实现),占用一个 fd;循环里 sock 每轮迭代离开作用域都会析构,linger 也设成了 0,但 fd 计数仍持续增长,在默认 fd 软上限(1024)下大约第 1023 个 socket 就会抛出 Too many open files(EMFILE)——析构没有真正释放资源;
  • 后续轮次 count 为 0,说明在先前 socket 已全部析构的情况下,新 socket 依然立即创建失败。

与 monitor 事件洪泛的区分 ​

Too many open files 在 ZeroMQ 应用里还有一类常见成因:启用了 socket monitor 的程序在连接反复断开重连时,监控事件大量积压,ZMQ IO 线程阻塞在事件发送路径上,socket 析构无法完成,内部的 eventfd 随之泄漏。两者表象相似,排查时值得先区分:如果 fd 增长集中在重连风暴期间、lsof 里堆积的是 eventfd,优先怀疑 monitor 事件洪泛;如果只是单纯的 inproc socket 创建销毁循环,则对应本文的 #4778。

小结 ​

inproc endpoint 的名字按 context 唯一、上限 256 字符来规划,跨模块复用一个 context 时注意命名空间隔离;频繁创建销毁 inproc socket 的场景要留意 #4778——上游未修复,绕开的方式是改用常驻 socket,避免短生命周期 socket 的循环创建。

最近更新

基于 VitePress 构建