从单 Reactor 线程池到 OneThreadOneLoop:高性能网络模型的演进

文章目录

  • 引言
  • [一、OneThreadOneLoop 模型的核心思想](#一、OneThreadOneLoop 模型的核心思想)
  • 二、需要新增的核心文件
    • [1. Channel.hpp:事件封装器](#1. Channel.hpp:事件封装器)
    • [2. EventLoop.hpp:线程内的事件循环](#2. EventLoop.hpp:线程内的事件循环)
    • [3. EventLoopThreadPool.hpp:事件循环线程池](#3. EventLoopThreadPool.hpp:事件循环线程池)
  • 三、需要修改的文件
    • [1. ReactorServer.hpp:主服务类](#1. ReactorServer.hpp:主服务类)
    • [2. ClientHandler.hpp:客户端连接处理器](#2. ClientHandler.hpp:客户端连接处理器)
  • 四、总结

引言

在前面的两篇文章中,我们实现了单 Reactor 单线程模型和单 Reactor 线程池模型。单 Reactor 线程池通过将 IO 操作分发到线程池处理,解决了单线程模型的性能瓶颈,但在高并发场景下,主线程的 Reactor 仍然可能成为新的瓶颈。本文将介绍如何将单 Reactor 线程池模型演进为更高效的 OneThreadOneLoop 模型,以及关键组件Channel的设计与价值。

一、OneThreadOneLoop 模型的核心思想

**OneThreadOneLoop(一个线程一个事件循环)**是高性能网络编程中的经典模型,其核心特点是:

  • 主线程(主 Reactor)仅负责监听新连接
  • 每个子线程拥有独立的事件循环(EventLoop)
  • 新连接通过轮询策略分配给子线程的 EventLoop
  • 连接的所有 IO 操作都在其绑定的子线程中处理,避免线程切换开销

相比单 Reactor 线程池,OneThreadOneLoop 的优势在于:

  1. 彻底避免多线程对同一连接的竞争
  2. 每个线程专注处理自己的事件循环,缓存友好性更好
  3. 简化线程同步逻辑,减少锁开销

二、需要新增的核心文件

1. Channel.hpp:事件封装器

Channel 是 OneThreadOneLoop 模型的关键组件,它封装了文件描述符(fd)关注的事件就绪的事件 以及对应的事件回调

cpp 复制代码
#ifndef __CHANNEL_HPP__
#define __CHANNEL_HPP__

#include <functional>
#include <sys/epoll.h>

// 事件回调类型定义
using EventCallback = std::function<void()>;

// Channel封装:负责管理单个文件描述符的IO事件
class Channel {
public:
    Channel(int fd) : _fd(fd), _events(0), _revents(0) {}
    ~Channel() = default;

    // 获取文件描述符
    int fd() const { return _fd; }
    
    // 设置/获取关注的事件
    void setEvents(uint32_t events) { _events = events; }
    uint32_t events() const { return _events; }
    
    // 设置/获取就绪的事件
    void setRevents(uint32_t revents) { _revents = revents; }
    uint32_t revents() const { return _revents; }
    
    // 设置事件回调
    void setReadCallback(EventCallback cb) { _read_cb = std::move(cb); }
    void setWriteCallback(EventCallback cb) { _write_cb = std::move(cb); }
    void setErrorCallback(EventCallback cb) { _error_cb = std::move(cb); }
    
    // 处理事件(核心方法)
    void handleEvent() {
        if (_revents & EPOLLERR) {
            if (_error_cb) _error_cb();
            return;
        }
        if (_revents & (EPOLLIN | EPOLLPRI)) {
            if (_read_cb) _read_cb();  // 触发读事件回调
        }
        if (_revents & EPOLLOUT) {
            if (_write_cb) _write_cb();  // 触发写事件回调
        }
    }
    
    // 事件操作辅助函数
    void enableReading() { _events |= EPOLLIN; }
    void enableWriting() { _events |= EPOLLOUT; }
    void disableWriting() { _events &= ~EPOLLOUT; }
    void disableAll() { _events = 0; }
    bool isWriting() const { return _events & EPOLLOUT; }

private:
    int _fd;                  // 关联的文件描述符
    uint32_t _events;         // 关注的事件(如EPOLLIN、EPOLLOUT)
    uint32_t _revents;        // 就绪的事件(由epoll返回)
    EventCallback _read_cb;   // 读事件回调
    EventCallback _write_cb;  // 写事件回调
    EventCallback _error_cb;  // 错误事件回调
};

#endif /* __CHANNEL_HPP__ */

Channel 的核心功能

  • 作为 fd 与事件循环的中间层,隔离了底层 IO 复用(如 epoll)的细节
  • 将事件(读 / 写 / 错误)与对应的处理逻辑(回调函数)绑定
  • 提供简洁的接口控制关注的事件(如启用 / 禁用读 / 写事件)

为什么说 Channel 并非必须,但却是最佳实践?

  • 理论上,我们可以直接在 EventLoop 中处理 fd 和事件回调,但会导致代码耦合严重
  • Channel 通过封装,实现了 "事件 - 回调" 的解耦,让 EventLoop 只需负责事件分发,无需关心具体业务
  • 在工程实践中,Channel 大幅提升了代码的可维护性和扩展性(例如新增事件类型时只需修改 Channel)

2. EventLoop.hpp:线程内的事件循环

每个线程拥有一个 EventLoop,负责监听该线程上所有 Channel 的事件,并分发处理。

cpp 复制代码
#ifndef __EVENT_LOOP_HPP__
#define __EVENT_LOOP_HPP__

#include "EpollDemultiplexer.hpp"
#include "Channel.hpp"
#include <map>
#include <thread>
#include <mutex>
#include <vector>

// 事件循环类:每个线程一个EventLoop
class EventLoop {
public:
    EventLoop() : _demux(), _thread_id(std::this_thread::get_id()), _running(false) {}
    ~EventLoop() { stop(); }

    // 禁用拷贝
    EventLoop(const EventLoop&) = delete;
    EventLoop& operator=(const EventLoop&) = delete;

    // 启动事件循环
    void start() {
        _running = true;
        loop();
    }

    // 停止事件循环
    void stop() {
        _running = false;
    }

    // 判断当前线程是否是事件循环线程(线程安全检查)
    bool isInLoopThread() const {
        return std::this_thread::get_id() == _thread_id;
    }

    // 注册Channel(将fd和事件注册到epoll)
    void updateChannel(Channel* channel) {
        int fd = channel->fd();
        if (_channels.find(fd) == _channels.end()) {
            _channels[fd] = channel;
            _demux.addEvent(fd, channel->events());
        } else {
            _demux.modifyEvent(fd, channel->events());
        }
    }

    // 移除Channel
    void removeChannel(Channel* channel) {
        int fd = channel->fd();
        if (_channels.find(fd) != _channels.end()) {
            _demux.removeEvent(fd);
            _channels.erase(fd);
        }
    }

private:
    // 事件循环主体
    void loop() {
        std::vector<epoll_event> events(1024);
        while (_running) {
            // 等待事件就绪
            int ready = _demux.waitEvents(events, 1000);
            if (ready < 0) continue;

            // 分发事件到对应Channel
            for (int i = 0; i < ready; ++i) {
                int fd = events[i].data.fd;
                auto it = _channels.find(fd);
                if (it != _channels.end()) {
                    it->second->setRevents(events[i].events);  // 设置就绪事件
                    it->second->handleEvent();  // 触发Channel的事件处理
                }
            }
        }
    }

private:
    EpollDemultiplexer _demux;                // 多路复用器(封装epoll)
    std::map<int, Channel*> _channels;        // fd到Channel的映射
    std::thread::id _thread_id;               // 事件循环所在线程ID
    bool _running;                            // 运行状态
};

#endif /* __EVENT_LOOP_HPP__ */

3. EventLoopThreadPool.hpp:事件循环线程池

管理多个 EventLoop 线程,负责线程的创建和轮询分配新连接。

cpp 复制代码
#ifndef __EVENT_LOOP_THREAD_POOL_HPP__
#define __EVENT_LOOP_THREAD_POOL_HPP__

#include "EventLoop.hpp"
#include <vector>
#include <memory>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>

// 事件循环线程
class EventLoopThread {
public:
    EventLoopThread() : _loop(nullptr), _running(false) {}
    
    ~EventLoopThread() {
        if (_running) {
            _running = false;
            if (_thread.joinable()) {
                _thread.join();
            }
        }
    }

    // 启动线程并返回事件循环
    EventLoop* startLoop() {
        _running = true;
        _thread = std::thread(&EventLoopThread::threadFunc, this);
        
        // 等待事件循环初始化完成
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock, [this]() { return _loop != nullptr; });
        
        return _loop;
    }

private:
    // 线程函数:运行事件循环
    void threadFunc() {
        EventLoop loop;  // 线程局部的事件循环
        
        {
            std::lock_guard<std::mutex> lock(_mutex);
            _loop = &loop;
            _cond.notify_one();  // 通知主线程循环已初始化
        }
        
        loop.start();  // 进入事件循环
        _loop = nullptr;
    }

private:
    EventLoop* _loop;                  // 事件循环
    bool _running;                     // 运行状态
    std::thread _thread;               // 线程
    std::mutex _mutex;                 // 互斥锁
    std::condition_variable _cond;     // 条件变量
};

// 事件循环线程池
class EventLoopThreadPool {
public:
    EventLoopThreadPool(size_t numThreads) : _num_threads(numThreads), _next_loop(0) {}
    
    // 启动线程池
    void start() {
        for (size_t i = 0; i < _num_threads; ++i) {
            auto thread = std::make_unique<EventLoopThread>();
            _loops.push_back(thread->startLoop());  // 保存每个线程的EventLoop
            _threads.push_back(std::move(thread));
        }
    }
    
    // 轮询获取下一个事件循环(负载均衡)
    EventLoop* getNextLoop() {
        if (_loops.empty()) return nullptr;
        
        EventLoop* loop = _loops[_next_loop];
        _next_loop = (_next_loop + 1) % _loops.size();  // 轮询索引
        return loop;
    }

private:
    size_t _num_threads;                          // 线程数量
    std::vector<EventLoop*> _loops;               // 事件循环列表
    std::vector<std::unique_ptr<EventLoopThread>> _threads;  // 线程列表
    size_t _next_loop;                            // 下一个要使用的事件循环索引
};

#endif /* __EVENT_LOOP_THREAD_POOL_HPP__ */

三、需要修改的文件

1. ReactorServer.hpp:主服务类

主要修改点:

  • 引入 EventLoopThreadPool 管理子线程的事件循环
  • 使用 Channel 管理监听套接字的事件(AcceptorChannel)
  • 新连接通过线程池分配到子线程的 EventLoop
cpp 复制代码
#ifndef __REACTOR_SERVER_HPP__
#define __REACTOR_SERVER_HPP__

#include "EpollDemultiplexer.hpp"
#include "ClientHandler.hpp"
#include "Socket.hpp"
#include "EventLoop.hpp"
#include "EventLoopThreadPool.hpp"
#include "Channel.hpp"
#include <map>
#include <memory>
#include <cstdint>
#include <atomic>

class ReactorServer {
public:
    ReactorServer(uint16_t port, int backlog = 1024, size_t thread_num = 4) 
        : _port(port), _backlog(backlog), _main_loop(),
          _thread_pool(thread_num), _is_running(false),
          _listen_socket(std::make_unique<TcpSocket>()) {}
          
    ~ReactorServer() {
        Stop();
    }

    // 初始化服务器
    bool Init() {
        if (_is_running) {
            std::cerr << "服务器已在运行中" << std::endl;
            return false;
        }

        try {
            // 创建并配置监听Socket
            _listen_socket->SocketOrDie();
            // 设置端口复用
            int opt = 1;
            setsockopt(_listen_socket->Fd(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
            _listen_socket->BindOrDie(_port);
            _listen_socket->ListenOrDie(_backlog);
            _listen_socket->SetNonBlocking();
            
            // 初始化Acceptor的Channel(核心修改)
            initAcceptor();
            
            // 启动事件循环线程池
            _thread_pool.start();
            
            return true;
        } catch (...) {
            std::cerr << "服务器初始化失败" << std::endl;
            return false;
        }
    }

    // 启动服务器
    bool Start() {
        if (_is_running) return false;
        _is_running = true;
        std::cout << "服务器启动成功,监听端口 " << _port << "..." << std::endl;
        _main_loop.start();  // 启动主事件循环
        return true;
    }

    // 停止服务器
    void Stop() {
        if (!_is_running) return;
        _is_running = false;
        _main_loop.stop();
        _clients.clear();
        _acceptor_channel.reset();
        _listen_socket->Close();
    }

private:
    // 初始化Acceptor的Channel
    void initAcceptor() {
        int listen_fd = _listen_socket->Fd();
        _acceptor_channel = std::make_unique<Channel>(listen_fd);
        
        // 设置读事件回调:接收新连接
        _acceptor_channel->setReadCallback([this]() {
            std::string client_ip;
            int client_fd = _listen_socket->Accept(&client_ip);
            if (client_fd >= 0) {
                onNewConnection(client_fd, client_ip);
            }
        });
        
        // 关注读事件
        _acceptor_channel->enableReading();
        _main_loop.updateChannel(_acceptor_channel.get());  // 注册到主循环
    }

    // 处理新连接(核心修改:分配到子线程的EventLoop)
    void onNewConnection(int client_fd, const std::string& client_ip) {
        std::cout << "新连接:IP=" << client_ip << ", fd=" << client_fd << std::endl;

        // 选择一个子事件循环
        EventLoop* loop = _thread_pool.getNextLoop();
        if (!loop) {
            close(client_fd);
            return;
        }

        // 创建客户端处理器并绑定到选中的事件循环
        auto client_handler = std::make_shared<ClientHandler>(
            client_fd, *loop,
            [this, client_ip](int fd, const std::string& data) {
                onMessage(fd, client_ip, data);
            },
            [this](int fd) {
                onClose(fd);
            });

        _clients[client_fd] = client_handler;
    }

    // 处理客户端消息
    void onMessage(int fd, const std::string& client_ip, const std::string& data) {
        std::cout << "[" << client_ip << "][fd=" << fd << "] 收到数据: " << data << std::endl;
        auto it = _clients.find(fd);
        if (it != _clients.end()) {
            it->second->sendData("服务器回显: " + data);
        }
    }

    // 处理客户端关闭
    void onClose(int fd) {
        std::cout << "客户端断开:fd=" << fd << std::endl;
        _clients.erase(fd);
    }

private:
    uint16_t _port;                          // 监听端口
    int _backlog;                            // 监听队列大小
    EventLoop _main_loop;                    // 主事件循环(处理新连接)
    EventLoopThreadPool _thread_pool;        // 事件循环线程池
    std::atomic<bool> _is_running;           // 运行状态标识
    std::unique_ptr<TcpSocket> _listen_socket;  // 监听Socket
    std::unique_ptr<Channel> _acceptor_channel; // Acceptor的Channel
    std::map<int, std::shared_ptr<ClientHandler>> _clients;  // 客户端处理器映射
};

#endif /* __REACTOR_SERVER_HPP__ */

2. ClientHandler.hpp:客户端连接处理器

主要修改点:

  • 使用 Channel 管理客户端 fd 的读写事件
  • 将 IO 事件绑定到所属子线程的 EventLoop
cpp 复制代码
#ifndef __CLIENT_HANDLER_HPP__
#define __CLIENT_HANDLER_HPP__

#include "EventHandler.hpp"
#include "EventLoop.hpp"
#include "Channel.hpp"
#include "Socket.hpp"
#include <functional>
#include <string>
#include <mutex>

// 客户端处理器:处理数据读写事件
class ClientHandler : public EventHandler {
public:
    using Ptr = std::shared_ptr<ClientHandler>;
    using MessageCallback = std::function<void(int, const std::string&)>;
    using CloseCallback = std::function<void(int)>;

    ClientHandler(int client_fd, EventLoop& loop, MessageCallback msg_cb, CloseCallback close_cb)
        : EventHandler(client_fd), _loop(loop), _msg_cb(std::move(msg_cb)),
          _close_cb(std::move(close_cb)), _client_socket(client_fd), _channel(client_fd) {
        // 初始化Channel回调(核心修改)
        _channel.setReadCallback([this]() { handleRead(); });
        _channel.setWriteCallback([this]() { handleWrite(); });
        _channel.setErrorCallback([this]() { handleError(); });
        
        // 开始关注读事件
        _channel.enableReading();
        _loop.updateChannel(&_channel);  // 注册到子线程的EventLoop
    }

    ~ClientHandler() {
        _loop.removeChannel(&_channel);  // 从事件循环中移除
    }

    // 处理读事件
    void handleRead() override {
        std::string data;
        ssize_t n = _client_socket.Recv(&data);

        if (n < 0) {
            if (errno != EAGAIN && errno != EWOULDBLOCK) {
                _close_cb(_fd);
            }
            return;
        }
        
        if (n == 0) {  // 客户端断开
            _close_cb(_fd);
            return;
        }
        
        _msg_cb(_fd, data);  // 触发消息回调
    }

    // 处理写事件(使用缓冲区实现异步发送)
    void handleWrite() override {
        std::lock_guard<std::mutex> lock(_buf_mutex);
        if (_send_buf.empty()) {
            _channel.disableWriting();  // 无数据可写,取消关注
            _loop.updateChannel(&_channel);
            return;
        }

        // 发送数据
        ssize_t n = _client_socket.Send(_send_buf);
        if (n > 0) {
            _send_buf.erase(0, n);
        }

        // 剩余数据继续监听可写事件
        if (!_send_buf.empty()) {
            _channel.enableWriting();
        } else {
            _channel.disableWriting();
        }
        _loop.updateChannel(&_channel);
    }

    void handleError() override {
        _close_cb(_fd);
    }

    // 发送数据(线程安全)
    void sendData(const std::string& data) {
        bool need_wakeup = false;
        {
            std::lock_guard<std::mutex> lock(_buf_mutex);
            need_wakeup = _send_buf.empty();  // 缓冲区为空时需要唤醒写事件
            _send_buf += data;
        }
        
        if (need_wakeup) {
            _channel.enableWriting();
            _loop.updateChannel(&_channel);
        }
    }

private:
    EventLoop& _loop;                // 所属的事件循环(子线程)
    MessageCallback _msg_cb;         // 消息处理回调
    CloseCallback _close_cb;         // 关闭回调
    std::string _send_buf;           // 待发送数据缓冲区
    std::mutex _buf_mutex;           // 缓冲区互斥锁
    TcpSocket _client_socket;        // 客户端Socket封装
    Channel _channel;                // 客户端Channel(核心组件)
};

#endif /* __CLIENT_HANDLER_HPP__ */

四、总结

从单 Reactor 线程池到 OneThreadOneLoop 的演进,核心变化是:

  1. 引入Channel组件,实现事件与回调的解耦
  2. 每个子线程拥有独立的EventLoop,负责本线程的事件循环
  3. 新连接通过线程池的轮询策略分配给子线程,实现负载均衡

这种模型在高并发场景下性能优势明显,也是许多高性能网络库(如 libevent、muduo)的核心设计。Channel 虽然不是实现事件驱动的必需组件,但它通过封装细节、解耦代码,成为了工程实践中的最佳选择。

相关推荐
七夜zippoe20 分钟前
CANN Runtime任务描述序列化与持久化源码深度解码
大数据·运维·服务器·cann
盟接之桥28 分钟前
盟接之桥说制造:引流品 × 利润品,全球电商平台高效产品组合策略(供讨论)
大数据·linux·服务器·网络·人工智能·制造
会员源码网1 小时前
理财源码开发:单语言深耕还是多语言融合?看完这篇不踩坑
网络·个人开发
米羊1212 小时前
已有安全措施确认(上)
大数据·网络
Fcy6482 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满2 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
主机哥哥2 小时前
阿里云OpenClaw部署全攻略,五种方案助你快速部署!
服务器·阿里云·负载均衡
ManThink Technology3 小时前
如何使用EBHelper 简化EdgeBus的代码编写?
java·前端·网络
珠海西格电力科技3 小时前
微电网能量平衡理论的实现条件在不同场景下有哪些差异?
运维·服务器·网络·人工智能·云计算·智慧城市
QT.qtqtqtqtqt3 小时前
未授权访问漏洞
网络·安全·web安全