zmq源码分析之消息可读通知机制

文章目录

    • 核心流程
    • 详细实现
      • [1. 网络数据接收与解析](#1. 网络数据接收与解析)
      • [2. 消息传递到 Socket](#2. 消息传递到 Socket)
      • [3. 用户层通知机制](#3. 用户层通知机制)
      • [4. zmq_poll 实现](#4. zmq_poll 实现)
    • 技术要点
      • [1. 消息完整性检测](#1. 消息完整性检测)
      • [2. 通知机制](#2. 通知机制)
      • [3. 线程安全](#3. 线程安全)
    • 完整流程图示
    • 总结

核心流程

ZeroMQ 检测完整消息可读并通知用户的完整流程

  1. 网络数据接收stream_engine 从网络读取数据
  2. 消息解析decoder 解析数据,检测完整消息
  3. 消息传递 :通过 pipe 传递给 session
  4. 消息存储socket 将消息存储在内部队列
  5. 通知用户:通过文件描述符的可读事件通知用户

详细实现

1. 网络数据接收与解析

stream_engine 接收数据

cpp 复制代码
bool zmq::stream_engine_base_t::in_event_internal ()
{
    // 读取网络数据
    const int rc = read (_inpos, bufsize);
    
    // 解码数据
    int rc = _decoder->decode (_inpos, _insize);
    
    // 消息就绪时
    if (_decoder->message_ready ()) {
        msg_t *msg = _decoder->msg ();
        // 写入 pipe
        _pipe->write (msg);
        _pipe->flush ();
    }
}

2. 消息传递到 Socket

session 处理

cpp 复制代码
void zmq::session_base_t::read_activated (zmq::pipe_t *pipe_)
{
    // 从 pipe 读取消息
    msg_t msg;
    while (pull_msg (&msg) == 0) {
        // 处理消息
        // 写入 socket 的 pipe
    }
}

socket 接收

cpp 复制代码
void zmq::socket_base_t::read_activated (zmq::pipe_t *pipe_)
{
    // 从 pipe 读取消息
    msg_t msg;
    while (pipe_->read (&msg)) {
        // 存储到内部队列
        _fq.push (&msg);
        
        // 通知用户(关键步骤)
        if (_fq.size () == 1) {
            // 第一次有消息时,通知用户
            signaler->send ();
        }
    }
}

3. 用户层通知机制

文件描述符通知

  • ZeroMQ 为每个 socket 创建一个通知文件描述符
  • 当有消息可读时,通过 signaler 向该文件描述符写入数据
  • 用户可以通过 ZMQ_FD 获取该文件描述符
  • 使用 poll/select 等系统调用监听该文件描述符

用户 API

cpp 复制代码
// 获取通知文件描述符
zmq_fd_t fd;
size_t fd_size = sizeof (fd);
zmq_getsockopt (socket, ZMQ_FD, &fd, &fd_size);

// 监听文件描述符
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
poll (&pfd, 1, -1);

// 检查是否有消息
uint32_t events;
size_t events_size = sizeof (events);
zmq_getsockopt (socket, ZMQ_EVENTS, &events, &events_size);
if (events & ZMQ_POLLIN) {
    // 有消息可读
    zmq_recv (socket, buffer, sizeof (buffer), 0);
}

4. zmq_poll 实现

socket_poller 检测

cpp 复制代码
int zmq::socket_poller_t::check_events (zmq::socket_poller_t::event_t *events_,
                                        int n_events_)
{
    int found = 0;
    for (items_t::iterator it = _items.begin (), end = _items.end ();
         it != end && found < n_events_; ++it) {
        if (it->socket) {
            // 检查 socket 事件
            uint32_t events;
            it->socket->getsockopt (ZMQ_EVENTS, &events, &events_size);
            if (it->events & events) {
                // 有事件发生
                events_[found].socket = it->socket;
                events_[found].events = it->events & events;
                ++found;
            }
        }
    }
    return found;
}

技术要点

1. 消息完整性检测

  • decoder:负责解析网络数据,检测消息边界
  • message_ready:当解析出完整消息时返回 true
  • 帧边界:ZeroMQ 消息由多个帧组成,decoder 负责检测帧边界

2. 通知机制

  • signaler:跨平台的信号机制,用于通知用户
  • 文件描述符:每个 socket 对应一个通知文件描述符
  • 事件状态 :通过 ZMQ_EVENTS 获取 socket 的事件状态

3. 线程安全

  • 线程安全 socket:使用 signaler 机制
  • 非线程安全 socket:直接使用文件描述符
  • poller:支持同时监听多个 socket

完整流程图示

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                        消息可读通知流程                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. 网络数据到达                                                   │
│     └─> stream_engine::in_event()                               │
│         └─> 读取网络数据                                          │
│         └─> decoder::decode() 解析数据                            │
│         └─> 检测消息完整性                                         │
│         └─> 完整消息就绪                                           │
│                                                                     │
│  2. 消息传递                                                       │
│     └─> _pipe->write(msg) 写入 pipe                               │
│     └─> session::read_activated() 处理                            │
│     └─> socket::read_activated() 接收                             │
│         └─> _fq.push(msg) 存储到队列                              │
│         └─> signaler->send() 发送通知                             │
│                                                                     │
│  3. 用户检测                                                       │
│     └─> poll() 检测文件描述符可读                                 │
│     └─> zmq_getsockopt(ZMQ_EVENTS) 检查事件                      │
│     └─> zmq_recv() 读取消息                                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

总结

ZeroMQ 检测完整消息可读并通知用户的机制

  1. 消息解析decoder 解析网络数据,检测完整消息
  2. 消息存储:消息存储在 socket 的内部队列
  3. 通知触发 :通过 signaler 触发文件描述符的可读事件
  4. 用户检测 :用户通过 pollzmq_poll 检测事件
  5. 消息读取 :调用 zmq_recv 读取消息

这种设计确保了用户能够及时得知有消息可读,同时避免了忙等带来的性能损耗,是 ZeroMQ 高效通信的重要基础。

相关推荐
潘正翔6 小时前
docker核心概念
linux·运维·服务器·docker·容器·centos·运维开发
renhongxia16 小时前
世界模型,是“空中楼阁”还是AGI的“最后一块拼图”?
运维·服务器·数据库·人工智能·算法·agi
Dovis(誓平步青云)8 小时前
远程办公软件文件传输实测:6 款工具的速度、稳定性和办公体验对比
linux·运维·服务器·后端·生成对抗网络
专注_每天进步一点点8 小时前
SLB(绑定弹性公网ip)-gateway-业务pod,gateway上出现reset by peer,业务pod上没有reset by peer
服务器·tcp/ip·gateway
CHANG_THE_WORLD8 小时前
TCP 三次握手彻底解析:SYN、ACK、SEQ、确认号与状态迁移
网络·网络协议·tcp/ip
ACP广源盛139246256739 小时前
GSV6155@ACP# 搭配 AI 服务器、AI PC 完整适配方案
大数据·服务器·人工智能·分布式·单片机·嵌入式硬件
DLYSB_10 小时前
基于 HTTP API 与 Modbus 协议的多源监控声光语音联动告警系统设计与实现
网络·网络协议·http·报警灯
Bobolink_10 小时前
手机端和电脑端的网络环境配置有什么不同?
网络·智能手机·电脑·网络环境
IT方大同11 小时前
linux简介
linux·运维·服务器
星恒讯工业路由器11 小时前
三大工业以太网协议选型对比
网络·物联网·ethercat·profinet·ethernet/ip·工业以太网协议