zmq源码分析之IO线程绑定时机

文章目录

    • 核心流程
    • 详细代码分析
      • [1. Socket 创建入口](#1. Socket 创建入口)
      • [2. IO 线程选择](#2. IO 线程选择)
      • [3. IO 线程选择逻辑](#3. IO 线程选择逻辑)
      • [4. Session 创建与绑定](#4. Session 创建与绑定)
      • [5. 连接建立时的 IO 线程绑定](#5. 连接建立时的 IO 线程绑定)
      • [6. Session 与 IO 线程关联](#6. Session 与 IO 线程关联)
    • 完整绑定流程
    • 技术要点
      • [1. IO 线程选择策略](#1. IO 线程选择策略)
      • [2. 绑定机制](#2. 绑定机制)
      • [3. 线程安全](#3. 线程安全)
    • 总结

核心流程

用户创建 socket 到绑定 IO 线程的完整流程

  1. 用户调用zmq_socket(ctx, type)
  2. 内部创建ctx->create_socket(type)
  3. IO 线程选择choose_io_thread(affinity)
  4. Session 创建:与 IO 线程关联
  5. 绑定完成:socket 成功绑定到 IO 线程

详细代码分析

1. Socket 创建入口

用户代码

cpp 复制代码
void *socket = zmq_socket(ctx, ZMQ_REP);

内部实现

cpp 复制代码
void *zmq_socket (void *ctx_, int type_)
{
    zmq::ctx_t *ctx = static_cast<zmq::ctx_t *> (ctx_);
    zmq::socket_base_t *s = ctx->create_socket (type_);
    // ...
    return s;
}

2. IO 线程选择

ctx_t::create_socket

cpp 复制代码
zmq::socket_base_t *zmq::ctx_t::create_socket (int type_)
{
    // ...
    if (unlikely (_starting)) {
        if (!start ())
            return NULL;
    }
    // ...
    // 选择 IO 线程
    io_thread_t *io_thread = choose_io_thread (0);
    if (!io_thread) {
        errno = EMTHREAD;
        return NULL;
    }
    // ...
    socket_base_t *s = socket_base_t::create (type_, this, slot, sid);
    // ...
}

3. IO 线程选择逻辑

ctx_t::choose_io_thread

cpp 复制代码
zmq::io_thread_t *zmq::ctx_t::choose_io_thread (uint64_t affinity_)
{
    // 遍历 IO 线程,选择负载最小的
    int min_load = -1;
    io_thread_t *selected_thread = NULL;
    
    for (io_threads_t::size_type i = 0; i != _io_threads.size (); i++) {
        if (!affinity_ || (affinity_ & (1ULL << i))) {
            int load = _io_threads[i]->get_load ();
            if (min_load == -1 || load < min_load) {
                min_load = load;
                selected_thread = _io_threads[i];
            }
        }
    }
    
    return selected_thread;
}

4. Session 创建与绑定

socket_base_t::create

cpp 复制代码
zmq::socket_base_t *zmq::socket_base_t::create (int type_, ctx_t *parent_, uint32_t tid_, int sid_)
{
    // 创建具体类型的 socket
    socket_base_t *s = NULL;
    switch (type_) {
        case ZMQ_REP:
            s = new (std::nothrow) rep_t (parent_, tid_, sid_);
            break;
        // ... 其他类型
    }
    // ...
    return s;
}

5. 连接建立时的 IO 线程绑定

当 socket 绑定或连接时

cpp 复制代码
int zmq::socket_base_t::bind (const char *endpoint_uri_)
{
    // ...
    if (protocol == protocol_name::tcp) {
        // 选择 IO 线程
        io_thread_t *io_thread = choose_io_thread (options.affinity);
        if (!io_thread) {
            errno = EMTHREAD;
            return -1;
        }
        
        // 创建 session
        session_base_t *session = session_base_t::create (io_thread, true, this, options, paddr);
        // ...
    }
    // ...
}

6. Session 与 IO 线程关联

session_base_t 构造

cpp 复制代码
zmq::session_base_t::session_base_t (zmq::io_thread_t *io_thread_,
                                    bool active_,
                                    zmq::socket_base_t *socket_,
                                    const options_t &options_,
                                    address_t *addr_) :
    own_t (io_thread_, options_),
    io_object_t (io_thread_),
    _active (active_),
    _pipe (NULL),
    _zap_pipe (NULL),
    _incomplete_in (false),
    _pending (false),
    _engine (NULL),
    _socket (socket_),
    _io_thread (io_thread_),
    _has_linger_timer (false),
    _addr (addr_)
{
    // session 现在与 IO 线程关联
}

完整绑定流程

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                        Socket 绑定 IO 线程流程                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. 用户创建 socket                                              │
│     └─> zmq_socket(ctx, type)                                     │
│         └─> ctx->create_socket(type)                              │
│             └─> choose_io_thread(affinity)                       │
│                 └─> 选择负载最小的 IO 线程                        │
│             └─> socket_base_t::create()                          │
│                 └─> 创建具体类型的 socket                        │
│                                                                     │
│  2. Socket 绑定或连接                                            │
│     └─> zmq_bind() / zmq_connect()                                │
│         └─> socket_base_t::bind() / connect()                     │
│             └─> choose_io_thread(options.affinity)                │
│             └─> session_base_t::create()                          │
│                 └─> 与 IO 线程关联                                │
│                 └─> attach_pipe()                               │
│                                                                     │
│  3. 引擎创建与注册                                                │
│     └─> 创建 stream_engine                                       │
│         └─> engine->plug(io_thread, session)                      │
│             └─> io_object_t::plug(io_thread)                      │
│             └─> add_fd(_s) 注册到 poller                          │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

技术要点

1. IO 线程选择策略

  • 负载均衡:选择负载最小的 IO 线程
  • 亲和性 :支持通过 ZMQ_AFFINITY 指定 IO 线程
  • 默认策略:轮询或基于负载

2. 绑定机制

  • Session 作为桥梁:连接 socket 和 IO 线程
  • Engine 注册:网络引擎注册到 IO 线程的 poller
  • Pipe 通信:socket 和 session 通过 pipe 通信

3. 线程安全

  • 命令传递:通过 mailbox 传递命令
  • 无锁设计:使用 ypipe 进行线程间通信
  • 事件驱动:IO 线程通过事件循环处理

总结

Socket 绑定到 IO 线程的过程

  1. 创建阶段:选择 IO 线程并创建 socket
  2. 连接阶段:创建 session 并与 IO 线程关联
  3. 运行阶段:引擎注册到 IO 线程的 poller

这种设计使得 ZeroMQ 能够:

  • 高效处理:IO 线程专门处理网络 I/O
  • 灵活扩展:通过增加 IO 线程数提高性能
  • 负载均衡:自动分配任务到负载较轻的线程

这是 ZeroMQ 高性能网络通信的重要基础!

相关推荐
阿标的博客2 小时前
Python学习(三):Python程序的运行方式
开发语言·python·学习
0xDevNull2 小时前
分布式事务实战指南:从理论到Seata落地
java·开发语言·后端
多年小白2 小时前
AI 日报 - 2026年4月25日(周六)
网络·人工智能·科技·深度学习·ai
t***5442 小时前
如何在 Dev-C++ 中配置 Clang 编译器
开发语言·c++
码云数智-大飞2 小时前
TLS 1.3的革新:更快的速度与更强的安全性
开发语言·php
南境十里·墨染春水2 小时前
linux学习进展 线程同步——条件变量
java·开发语言·学习
Johnstons2 小时前
网络诊断工具怎么选:从监控告警到抓包定位的完整方法论
服务器·网络·php·es·抓包分析·网络诊断工具选型与排障方法
sghuter2 小时前
数字资源分发的技术架构与未来趋势
c语言·开发语言·后端·青少年编程
惊鸿若梦一书生2 小时前
《Python 高阶教程》016|偏函数与柯里化:把复杂调用拆成更简单的组合
linux·网络·python