Linux网络学习【Reactor反应堆模式】


🔥承渊政道: 个人主页
❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》 《C++知识内容》 《Linux系统与网络知识》 《算法刷题指南》 《测评文章活动推广》 《大模型语言路线学习》 《MySQL数据库学习》 《Python知识内容》 《cpolar知识学习》
✨逆境不吐心中苦,顺境不忘来时路!✨ 🎬 博主简介:

在学习 select、poll 和 epoll 之后,我们已经能够通过就绪通知管理多个连接.但从一个能够收发数据的事件循环,到结构清晰、便于扩展的网络服务器,还需要解决新的问题:新连接、读写和异常事件应该如何分发?不同连接的状态与缓冲区由谁管理?网络通信与业务逻辑又该如何分离? Reactor 反应堆模式,正是组织这些职责的一种设计思路.Reactor 以 "等待就绪事件 → 分发事件 → 执行对应处理逻辑" 为核心,将事件监听与具体处理分离,使连接接收、数据读写和异常处理能够围绕统一的事件循环展开.需要明确的是,Reactor 是一种事件驱动的设计模式,并不是某个系统调用,也不等同于异步 IO .就绪通知之后,程序仍需执行实际读写;如果在事件循环中执行耗时或阻塞操作,其他连接的响应仍会受到影响.因此,理解Reactor不仅要看事件如何流转,还要关注任务分工、连接生命周期与资源管理.本文将以 "事件驱动思想 → 核心组件 → 线程模型 → 代码实践" 为主线,梳理事件源、多路复用器、事件分发器与处理器之间的协作关系,对比单Reactor单线程、单Reactor多线程及主从 Reactor 的组织方式.随后结合epoll与非阻塞读写,逐步分析事件注册、回调分发、缓冲区管理和连接清理,将分散的网络接口组织成完整的服务器结构,为后续学习高并发网络服务与事件驱动框架打下基础.

目录

一、Reactor反应堆模式代码

Reactor模式是⼀种事件驱动的编程模式,⽤于⾼效地处理并发I/O操作.它通过⼀个或多个事件循环(Event Loop)来监听和处理各种事件(如⽹络请求、定时器事件等),从⽽实现⾼效的并发处

理,⽽⽆需为每个连接创建⼀个线程或进程.

cpp 复制代码
//Accepter.hpp
#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cerrno>
#include "Common.hpp"
#include "Log.hpp"
#include "Connection.hpp"
#include "HandlerConnection.hpp"

class Accepter // 连接管理器
{
public:
    Accepter()
    {
    }
    // ET模式,你怎么知道,只有一条连接到来了呢?
    void AccepterConnection(/*this, */ Connection *conn) // conn: listensocket对应的conn
    {
        errno = 0;
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = ::accept(conn->SockFd(), (struct sockaddr *)&peer, &len);
            if (sockfd > 0)
            {
                lg.LogMessage(Info, "get a new link, sockfd is: %d\n", sockfd);
                SetNonBlock(sockfd);
                auto recver = std::bind(&HandlerConnection::Recver, std::placeholders::_1);
                auto sender = std::bind(&HandlerConnection::Sender, std::placeholders::_1);
                auto excepter = std::bind(&HandlerConnection::Excepter, std::placeholders::_1);
                Connection *normal_conn = ConnectionFactory::BuildNormalConnection(sockfd,
                    recver, sender, excepter, EPOLLIN|EPOLLET, conn->_R);
                conn->_R->AddConnection(normal_conn);
            }
            else
            {
                if (errno == EAGAIN)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    lg.LogMessage(Warning, "get a new link error\n");
                    break;
                }
            }
        }
    }
    ~Accepter()
    {
    }
};
cpp 复制代码
//Calculate.hpp
#pragma once

#include <iostream>
#include <memory>
#include "Protocol.hpp"

// 业务
namespace CalCulateNS
{
    enum
    {
        Success = 0,
        DivZeroErr,
        ModZeroErr,
        UnKnowOper
    };

    class Calculate
    {
    public:
        Calculate() {}
        std::shared_ptr<Protocol::Response> Cal(std::shared_ptr<Protocol::Request> req)
        {
            std::shared_ptr<Protocol::Response> resp = factory.BuildResponse();
            resp->SetCode(Success);
            switch (req->GetOper())
            {
            case '+':
                resp->SetResult(req->GetX() + req->GetY());
                break;
            case '-':
                resp->SetResult(req->GetX() - req->GetY());
                break;
            case '*':
                resp->SetResult(req->GetX() * req->GetY());
                break;
            case '/':
            {
                if (req->GetY() == 0)
                {
                    resp->SetCode(DivZeroErr);
                }
                else
                {
                    resp->SetResult(req->GetX() / req->GetY());
                }
            }
            break;
            case '%':
            {
                if (req->GetY() == 0)
                {
                    resp->SetCode(ModZeroErr);
                }
                else
                {
                    resp->SetResult(req->GetX() % req->GetY());
                }
            }
            break;
            default:
                resp->SetCode(UnKnowOper);
                break;
            }

            return resp;
        }
        ~Calculate() {}

    private:
        Protocol::Factory factory;
    };
} // namespace CalCulate
cpp 复制代码
//Common.hpp
#pragma once

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cstdlib>
#include <cerrno>
#include "Log.hpp"

// 对指定的fd设置非阻塞
void SetNonBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if(fl < 0)
    {
        exit(0);
    }
    lg.LogMessage(Info, "set %d been non block\n", fd);
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}
cpp 复制代码
//Connection.hpp
#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

class Connection;
class TcpServer;

using func_t = std::function<void(Connection *)>;

class Connection
{
public:
    Connection(int sockfd, uint32_t events, TcpServer *R)
        : _sockfd(sockfd), _events(events), _R(R)
    {
    }
    void RegisterCallback(func_t recver, func_t sender, func_t excepter)
    {
        _recver = recver;
        _sender = sender;
        _excepter = excepter;
    }
    void AddInBuffer(std::string buffer)
    {
        _inbuffer += buffer; // 追加到inbuffer中
    }
    void AddOutBuffer(const std::string &buffer)
    {
        _outbuffer += buffer;
    }
    bool OutBufferEmpty()
    {
        return _outbuffer.empty();
    }
    int SockFd()
    {
        return _sockfd;
    }
    uint32_t Events()
    {
        return _events;
    }
    void SetEvents(uint32_t events)
    {
        _events = events;
    }
    void SetClient(const struct sockaddr_in c)
    {
        _client = c;
    }
    std::string &InBuffer()
    {
        return _inbuffer;
    }
    std::string &OutBuffer()
    {
        return _outbuffer;
    }
    void Close()
    {
        ::close(_sockfd);
    }
    ~Connection()
    {
    }

private:
    // 对应的sockfd
    int _sockfd;
    // 对应的缓冲区
    std::string _inbuffer;  // _sockfd 接受缓冲区, 暂时用string代替
    std::string _outbuffer; // _sockfd 发送缓冲区

    // 关心的事件
    uint32_t _events;
    // 维护一下client的ip和port信息
    struct sockaddr_in _client;

public:
    // 对特定connection进行处理的回调函数
    func_t _recver;
    func_t _sender;
    func_t _excepter;
    // TcpServer的回指指针 - TODO
    TcpServer *_R;
};

class ConnectionFactory
{
public:
    static Connection *BuildListenConnection(int listensock, func_t recver, uint32_t events, TcpServer *R)
    {
        Connection *conn = new Connection(listensock, events, R);
        conn->RegisterCallback(recver, nullptr, nullptr);
        return conn;
    }
    static Connection *BuildNormalConnection(int sockfd,
                                             func_t recver,
                                             func_t sender,
                                             func_t excepter,
                                             uint32_t events,
                                             TcpServer *R)
    {
        Connection *conn = new Connection(sockfd, events, R);
        conn->RegisterCallback(recver, sender, excepter);
        return conn;
    }
};
cpp 复制代码
//Epoller.hpp
#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <set>
#include <unistd.h>
#include <sys/epoll.h>
#include "Log.hpp"

namespace EpollerModule
{
    const static int defualtepfd = -1;
    const static int size = 128;

    class Epoller
    {
    public:
        Epoller() : _epfd(defualtepfd)
        {
        }
        void InitEpoller()
        {
            _epfd = ::epoll_create(size);
            if (defualtepfd == _epfd)
            {
                lg.LogMessage(Fatal, "epoll_create error, %s : %d\n", strerror(errno), errno);
                exit(4);
            }
            lg.LogMessage(Info, "epoll_create success, epfd: %d\n", _epfd);
        }
        void AddEvent(int sockfd, uint32_t events)
        {
            // for test
            fd_list.insert(sockfd);

            struct epoll_event ev;
            ev.events = events;
            ev.data.fd = sockfd; // ??, 不是给内核的, 给用户的

            int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
            if (n < 0)
            {
                lg.LogMessage(Error, "epoll_ctl add error, %s : %d\n", strerror(errno), errno);
            }
        }
        void ModEvent(int sockfd, uint32_t events)
        {
            struct epoll_event ev;
            ev.events = events;
            ev.data.fd = sockfd;
            int n = ::epoll_ctl(_epfd, EPOLL_CTL_MOD, sockfd, &ev);
            if (n < 0)
            {
                lg.LogMessage(Error, "epoll_ctl mod error, %s : %d\n", strerror(errno), errno);
            }
        }
        void DelEvent(int sockfd)
        {
            // for test
            fd_list.erase(sockfd);

            int n = ::epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
            if (n < 0)
            {
                lg.LogMessage(Error, "epoll_ctl del error, %s : %d\n", strerror(errno), errno);
            }
        }
        void DebugFdList()
        {
            std::cout << "fd list is : ";
            for (auto &fd : fd_list)
            {
                std::cout << fd << " ";
            }
            std::cout << std::endl;
        }
        int Wait(struct epoll_event *revs, int maxevents, int timeout)
        {
            int n = ::epoll_wait(_epfd, revs, maxevents, timeout);
            return n;
        }
        ~Epoller()
        {
            if (_epfd >= 0)
            {
                close(_epfd);
            }
            lg.LogMessage(Info, "epoll close success\n");
        }

    private:
        int _epfd;
        std::set<int> fd_list;
    };
}
cpp 复制代码
//HandlerConnection.hpp
#pragma once

#include <iostream>
#include <cerrno>
#include "Connection.hpp"
#include "Protocol.hpp"
#include "Calculate.hpp"
#include "Log.hpp"

using namespace Protocol;
using namespace CalCulateNS;

const static int buffer_size = 1024;

class HandlerConnection
{
public:
    static void HandlerRequest(Connection *conn)
    {
        std::string &inbuffer = conn->InBuffer();
        std::string message; // 表示一个符合协议的一个完整的报文
        Calculate calulate;  // 负责业务处理
        Factory factory;
        auto req = factory.BuildRequest();
        // 1. 明确报文边界,解决粘报问题
        while (Decode(inbuffer, &message))
        {
            // message 一定是一个完整的报文,符合协议的!
            // 2. 反序列化
            if (!req->Deserialize(message))
                continue;
            // 3. 业务处理
            auto resp = calulate.Cal(req);
            // 4. 对相应进行序列化
            std::string responseStr;
            resp->Serialize(&responseStr);
            // 5. 封装完整报文
            responseStr = Encode(responseStr);
            // 6. 将应答全部追加到outbuffer中
            conn->AddOutBuffer(responseStr);
        }
        // 考虑发送的问题了
        if (!conn->OutBufferEmpty())
        {
            conn->_sender(conn); // 对写事件,直接发!!!--- 不代表能全部发完!
        }
    }
    // 在这里读取的时候,我们关系数据是什么格式?协议是什么样子的吗?
    // 不关心!!!我们只负责把本轮属于完全读取完毕 --- 把读到的字节流数据,交给上层 --- 由上层进行分析处理
    static void Recver(Connection *conn)
    {
        errno = 0;
        // 读取流程
        char buffer[1024];
        while (true)
        {
            ssize_t n = recv(conn->SockFd(), buffer, sizeof(buffer) - 1, 0); // 非阻塞读取
            if (n > 0)
            {
                buffer[n] = 0;
                conn->AddInBuffer(buffer);
            }
            else
            {
                // std::cout << "................... errno:" << errno << std::endl;
                if (errno == EAGAIN)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    // 真正的读取错误
                    conn->_excepter(conn); // 直接回调自己的异常处理就可以了!
                    return;
                }
            }
        }
        std::cout << "sockfd# " << conn->SockFd() << ":\n"
                  << conn->InBuffer() << std::endl;
        // 尝试分析处理报文 -- 半个,一个半, 10个,11个半
        HandlerRequest(conn);
    }
    static void Sender(Connection *conn)
    {
        errno = 0;
        std::string &outbuffer = conn->OutBuffer();
        while (true)
        {
            ssize_t n = send(conn->SockFd(), outbuffer.c_str(), outbuffer.size(), 0);
            if (n >= 0)
            {
                outbuffer.erase(0, n); // 已经发给OS的,就直接移除了 // conn->remove(n);
                if (outbuffer.empty())
                    break;
            }
            else
            {
                if (errno == EAGAIN)
                    break; // 只有这里,才会正常退出
                else if (errno == EINTR)
                    continue;
                else
                {
                    conn->_excepter(conn);
                    return;
                }
            }
        }

        // 走到这里,意味着什么?我们本轮发满了,但是数据可能没发完,为什么没发完呢?
        // 开启对conn->SockFd() EPOLLOUT的关心!!!!!, 如何开启对于特定一个connection对应的写事件关心呢???
        if (!conn->OutBufferEmpty())
        {
            conn->_R->EnableReadWrite(conn->SockFd(), true, true);
        }
        else
        {
            conn->_R->EnableReadWrite(conn->SockFd(), true, false);
        }
    }
    static void Excepter(Connection *conn)
    {
        lg.LogMessage(Info, "connection erase done, who: %d\n", conn->SockFd());
        errno = 0;
        // 从epoll中移除对conn->Sockfd的关心
        // unordered_map 移除conn
        conn->_R->RemoveConnection(conn->SockFd());
        // 关闭conn->Sockfd
        conn->Close();
        // delete conn
        delete conn;
    }
};
cpp 复制代码
//Log.hpp
#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

enum
{
    Debug = 0,
    Info,
    Warning,
    Error,
    Fatal
};

enum
{
    Screen = 10,
    OneFile,
    ClassFile
};

std::string LevelToString(int level)
{
    switch (level)
    {
    case Debug:
        return "Debug";
    case Info:
        return "Info";
    case Warning:
        return "Warning";
    case Error:
        return "Error";
    case Fatal:
        return "Fatal";
    default:
        return "Unknown";
    }
}

const int defaultstyle = Screen;
const std::string default_filename = "log.";
const std::string logdir = "log";

class Log
{
public:
    Log() : style(defaultstyle), filename(default_filename)
    {
        mkdir(logdir.c_str(), 0775);
    }
    void Enable(int sty) //
    {
        style = sty;
    }
    std::string TimeStampExLocalTime()
    {
        time_t currtime = time(nullptr);
        struct tm *curr = localtime(&currtime);
        char time_buffer[128];
        snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
                 curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday,
                 curr->tm_hour, curr->tm_min, curr->tm_sec);
        return time_buffer;
    }
    void WriteLogToOneFile(const std::string &logname, const std::string &message)
    {
        umask(0);
        int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
        if(fd < 0) return;
        write(fd, message.c_str(), message.size());
        close(fd);
        // std::ofstream out(logname);
        // if (!out.is_open())
        //     return;
        // out.write(message.c_str(), message.size());
        // out.close();
    }
    void WriteLogToClassFile(const std::string &levelstr, const std::string &message)
    {
        std::string logname = logdir;
        logname += "/";
        logname += filename;
        logname += levelstr;
        WriteLogToOneFile(logname, message);
    }

    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    void WriteLog(const std::string &levelstr, const std::string &message)
    {
        pthread_mutex_lock(&lock);
        switch (style)
        {
        case Screen:
            std::cout << message;
            break;
        case OneFile:
            WriteLogToClassFile("all", message);
            break;
        case ClassFile:
            WriteLogToClassFile(levelstr, message);
            break;
        default:
            break;
        }
        pthread_mutex_unlock(&lock);
    }
    void LogMessage(int level, const char *format, ...) // 类C的一个日志接口
    {
        char leftbuffer[1024];
        std::string levelstr = LevelToString(level);
        std::string currtime = TimeStampExLocalTime();
        std::string idstr = std::to_string(getpid());

        char rightbuffer[1024];
        va_list args; // char *, void *
        va_start(args, format);
        // args 指向了可变参数部分
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);
        va_end(args); // args = nullptr;
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s] ",
                 levelstr.c_str(), currtime.c_str(), idstr.c_str());

        std::string loginfo = leftbuffer;
        loginfo += rightbuffer;
        WriteLog(levelstr, loginfo);
    }
    // void operator()(int level, const char *format, ...)
    // {
    //     LogMessage(int level, const char *format, ...)
    // }
    ~Log() {}

private:
    int style;
    std::string filename;
};

Log lg;

class Conf
{
public:
    Conf()
    {
        lg.Enable(Screen);
    }
    ~Conf()
    {}
};

Conf conf;
cpp 复制代码
//Main.cc
#include "TcpServer.hpp"
#include "Socket.hpp"
#include "Accepter.hpp"
#include <memory>
#include <iostream>

using namespace NetWorkModule;

static const int gbacklog = 32;

void Usage(std::string process)
{
    std::cerr << "Usage: " << process << " port" << std::endl;
}

void Test(Connection *conn)
{
    std::cout << "get event, sockfd is : " << conn->SockFd() << std::endl;
}

// 这个代码是什么鬼???Reator : 反应堆模式,半同步半异步模式,Linux高并发服务器最常见的模式(没有之一)
// ./epoll_server 8888
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }

    int port = std::stoi(argv[1]);

    // 1. 创建listensock
    std::unique_ptr<Socket> listensock = std::make_unique<TcpSocket>(); // 3
    listensock->BuildListenSocketMethod(port, gbacklog);
    
    // 2. 创建tcpserver
    std::unique_ptr<TcpServer> svr = std::make_unique<TcpServer>();
    std::unique_ptr<Accepter> accepter = std::make_unique<Accepter>();
    auto listen_recver = std::bind(&Accepter::AccepterConnection, accepter.get(), std::placeholders::_1);

    // 4. 构建listen对应的connection,添加到tcpserver中
    Connection *conn = ConnectionFactory::BuildListenConnection(listensock->GetSockFd(),
                                                                listen_recver,
                                                                EPOLLIN | EPOLLET,
                                                                svr.get());
    svr->AddConnection(conn);

    // 5. 开始事件派发
    svr->Dispatcher(); // 开始事件派发啦
    return 0;
}
cpp 复制代码
//Makefile
epoll_server:Main.cc
	g++ -o $@ $^ -std=c++14 #-ljsoncpp
.PHONY:clean
clean:
	rm -f epoll_server
cpp 复制代码
//Protocol.hpp
#pragma once

#include <iostream>
#include <memory>
#include <jsoncpp/json/json.h>

#define SelfDefine 1

namespace Protocol
{
    // 问题
    // 1. 结构化数据的序列和反序列化
    // 2. 还要解决用户区分报文边界 --- 数据包粘报问题

    // 讲法
    // 1. 自定义协议
    // 2. 成熟方案序列和反序列化

    // 总结:
    // 我们今天定义了几组协议呢??我们可以同时存在多个协议吗???可以
    // "protocol_code\nlen\nx op y\n" : \n不属于报文的一部分,约定

    const std::string ProtSep = " ";
    const std::string LineBreakSep = "\r\n"; // telnet 敲击回车:\n->\r\n

    // "len\nx op y\n" : \n不属于报文的一部分,约定
    std::string Encode(const std::string &message)
    {
        std::string len = std::to_string(message.size());
        std::string package = len + LineBreakSep + message + LineBreakSep;
        return package;
    }
    // "len\nx op y\n" : \n不属于报文的一部分,约定
    // 我无法保证package就是一个独立的完整的报文
    // "l
    // "len
    // "len\n
    // "len\nx
    // "len\nx op
    // "len\nx op y
    // "len\nx op y\n"
    // "len\nx op y\n""len
    // "len\nx op y\n""len\n
    // "len\nx op
    // "len\nx op y\n""len\nx op y\n"
    // "len\nresult code\n""len\nresult code\n"

    bool Decode(std::string &package, std::string *message)
    {
        // 除了解包,我还想判断报文的完整性, 能否正确处理具有"边界"的报文
        auto pos = package.find(LineBreakSep); // 5\r\n -> 5\r
        if (pos == std::string::npos)
            return false;
        std::string lens = package.substr(0, pos);
        int messagelen = std::stoi(lens);
        int total = lens.size() + messagelen + 2 * LineBreakSep.size();
        if (package.size() < total)
            return false;
        // 至少package内部一定有一个完整的报文了!
        *message = package.substr(pos + LineBreakSep.size(), messagelen);
        package.erase(0, total);
        return true;
    }

    class Request
    {
    public:
        Request() : _data_x(0), _data_y(0), _oper(0)
        {
        }
        Request(int x, int y, char op) : _data_x(x), _data_y(y), _oper(op)
        {
        }
        void Debug()
        {
            std::cout << "_data_x: " << _data_x << std::endl;
            std::cout << "_data_y: " << _data_y << std::endl;
            std::cout << "_oper: " << _oper << std::endl;
        }
        void Inc()
        {
            _data_x++;
            _data_y++;
        }
        // 结构化数据->字符串
        bool Serialize(std::string *out)
        {
#ifdef SelfDefine // 条件编译
            *out = std::to_string(_data_x) + ProtSep + _oper + ProtSep + std::to_string(_data_y);
            return true;
#else
            Json::Value root;
            root["datax"] = _data_x;
            root["datay"] = _data_y;
            root["oper"] = _oper;
            Json::FastWriter writer;
            *out = writer.write(root);
            return true;
#endif
        }
        bool Deserialize(std::string &in) // "x op y" [)
        {
#ifdef SelfDefine
            auto left = in.find(ProtSep);
            if (left == std::string::npos)
                return false;
            auto right = in.rfind(ProtSep);
            if (right == std::string::npos)
                return false;

            _data_x = std::stoi(in.substr(0, left));
            _data_y = std::stoi(in.substr(right + ProtSep.size()));
            std::string oper = in.substr(left + ProtSep.size(), right - (left + ProtSep.size()));
            if (oper.size() != 1)
                return false;
            _oper = oper[0];
            return true;
#else
            Json::Value root;
            Json::Reader reader;
            bool res = reader.parse(in, root);
            if(res)
            {
                _data_x = root["datax"].asInt();
                _data_y = root["datay"].asInt();
                _oper = root["oper"].asInt();
            }
            return res;
#endif
        }
        int GetX() { return _data_x; }
        int GetY() { return _data_y; }
        char GetOper() { return _oper; }

    private:
        // _data_x _oper _data_y
        // 报文的自描述字段
        // "len\nx op y\n" : \n不属于报文的一部分,约定
        // 很多工作都是在做字符串处理!
        int _data_x; // 第一个参数
        int _data_y; // 第二个参数
        char _oper;  // + - * / %
    };

    class Response
    {
    public:
        Response() : _result(0), _code(0)
        {
        }
        Response(int result, int code) : _result(result), _code(code)
        {
        }
        bool Serialize(std::string *out)
        {
#ifdef SelfDefine

            *out = std::to_string(_result) + ProtSep + std::to_string(_code);
            return true;
#else
            Json::Value root;
            root["result"] = _result;
            root["code"] = _code;
            Json::FastWriter writer;
            *out = writer.write(root);
            return true;
#endif
        }
        bool Deserialize(std::string &in) // "_result _code" [)
        {
#ifdef SelfDefine
            auto pos = in.find(ProtSep);
            if (pos == std::string::npos)
                return false;
            _result = std::stoi(in.substr(0, pos));
            _code = std::stoi(in.substr(pos + ProtSep.size()));
            return true;
#else
            Json::Value root;
            Json::Reader reader;
            bool res = reader.parse(in, root);
            if(res)
            {
                _result = root["result"].asInt();
                _code = root["code"].asInt();
            }
            return res;
#endif
        }
        void SetResult(int res) { _result = res; }
        void SetCode(int code) { _code = code; }
        int GetResult() { return _result; }
        int GetCode() { return _code; }

    private:
        // "len\n_result _code\n"
        int _result; // 运算结果
        int _code;   // 运算状态
    };

    // 简单的工厂模式,建造类设计模式
    class Factory
    {
    public:
        std::shared_ptr<Request> BuildRequest()
        {
            std::shared_ptr<Request> req = std::make_shared<Request>();
            return req;
        }
        std::shared_ptr<Request> BuildRequest(int x, int y, char op)
        {
            std::shared_ptr<Request> req = std::make_shared<Request>(x, y, op);
            return req;
        }
        std::shared_ptr<Response> BuildResponse()
        {
            std::shared_ptr<Response> resp = std::make_shared<Response>();
            return resp;
        }
        std::shared_ptr<Response> BuildResponse(int result, int code)
        {
            std::shared_ptr<Response> req = std::make_shared<Response>(result, code);
            return req;
        }
    };
}
cpp 复制代码
//Socket.hpp
#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "Common.hpp"

#define Convert(addrptr) ((struct sockaddr *)addrptr)

namespace NetWorkModule
{
    const static int defaultsockfd = -1;
    const int backlog = 5;

    enum
    {
        SocketError = 1,
        BindError,
        ListenError,
    };

    // 封装一个基类,Socket接口类
    // 设计模式:模版方法类
    class Socket
    {
    public:
        virtual ~Socket() {}
        virtual void CreateSocketOrDie() = 0;
        virtual void BindSocketOrDie(uint16_t port) = 0;
        virtual void ListenSocketOrDie(int backlog) = 0;
        virtual int AcceptConnection(std::string *peerip, uint16_t *peerport) = 0;
        virtual bool ConnectServer(std::string &serverip, uint16_t serverport) = 0;
        virtual int GetSockFd() = 0;
        virtual void SetSockFd(int sockfd) = 0;
        virtual void CloseSocket() = 0;
        virtual bool Recv(std::string *buffer, int size) = 0;
        virtual void Send(std::string &send_str) = 0;
        virtual void ReUseAddr() = 0;
        // TODO
    public:
        void BuildListenSocketMethod(uint16_t port, int backlog)
        {
            CreateSocketOrDie();
            ReUseAddr();
            BindSocketOrDie(port);
            ListenSocketOrDie(backlog);
        }
        bool BuildConnectSocketMethod(std::string &serverip, uint16_t serverport)
        {
            CreateSocketOrDie();
            return ConnectServer(serverip, serverport);
        }
        void BuildNormalSocketMethod(int sockfd)
        {
            SetSockFd(sockfd);
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd)
        {
        }
        ~TcpSocket()
        {
        }
        void CreateSocketOrDie() override
        {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
                exit(SocketError);
            SetNonBlock(_sockfd); // 将listensock设置成为非阻塞
        }
        void BindSocketOrDie(uint16_t port) override
        {
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_addr.s_addr = INADDR_ANY;
            local.sin_port = htons(port);

            int n = ::bind(_sockfd, Convert(&local), sizeof(local));
            if (n < 0)
                exit(BindError);
        }
        void ListenSocketOrDie(int backlog) override
        {
            int n = ::listen(_sockfd, backlog);
            if (n < 0)
                exit(ListenError);
        }
        int AcceptConnection(std::string *peerip, uint16_t *peerport) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int newsockfd = ::accept(_sockfd, Convert(&peer), &len);
            if (newsockfd < 0)
                return -1;
            *peerport = ntohs(peer.sin_port);
            *peerip = inet_ntoa(peer.sin_addr);
            return newsockfd;
        }
        bool ConnectServer(std::string &serverip, uint16_t serverport) override
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(serverip.c_str());
            server.sin_port = htons(serverport);

            int n = ::connect(_sockfd, Convert(&server), sizeof(server));
            if (n == 0)
                return true;
            else
                return false;
        }
        int GetSockFd() override
        {
            return _sockfd;
        }
        void SetSockFd(int sockfd) override
        {
            _sockfd = sockfd;
        }
        void CloseSocket() override
        {
            if (_sockfd > defaultsockfd)
                ::close(_sockfd);
        }
        bool Recv(std::string *buffer, int size) override
        {
            char inbuffer[size];
            ssize_t n = recv(_sockfd, inbuffer, size - 1, 0);
            if (n > 0)
            {
                inbuffer[n] = 0;
                *buffer += inbuffer; // 故意拼接的
                return true;
            }
            else if (n == 0)
                return false;
            else
                return false;
        }
        void Send(std::string &send_str) override
        {
            // 多路转接我们在统一说
            send(_sockfd, send_str.c_str(), send_str.size(), 0);
        }
        void ReUseAddr() override
        {
            int opt = 1;
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        }

    private:
        int _sockfd;
    };

}
cpp 复制代码
//TcpServer.hpp
#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include "Socket.hpp"
#include "Connection.hpp"
#include "Epoller.hpp"

using namespace EpollerModule;

#define EPOLL_BLOCK -1

class TcpServer;

// 只要管理一堆的Connection, 它就是Reactor!
// class Reactor --- 核心的部分
class TcpServer
{
    static const int gmaxevents = 64;

public:
    TcpServer() : _isrunning(false), _timeout(EPOLL_BLOCK)
    {
        _epoller.InitEpoller();
    }
    bool IsConnectionExists(int sockfd)
    {
        auto iter = _connections.find(sockfd);
        if (iter == _connections.end())
            return false;
        else
            return true;
    }
    void AddConnection(Connection *conn)
    {
        if (!conn)
            return;
        if (IsConnectionExists(conn->SockFd()))
            return;
        // 1. 添加TcpServer unordered_map
        _connections.insert(std::make_pair(conn->SockFd(), conn));
        lg.LogMessage(Info, "add a new connection success, sockfd: %d\n", conn->SockFd());
        // 2. 让epoller管理起来
        _epoller.AddEvent(conn->SockFd(), conn->Events());
    }
    void EnableReadWrite(int sockfd, bool readable, bool writeable)
    {
        if (!IsConnectionExists(sockfd))
            return;
        uint32_t events = ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        // 1. 修改connection自己关心的事件
        _connections[sockfd]->SetEvents(events);
        // 2. 修改epoller, 写透到内核
        _epoller.ModEvent(sockfd, _connections[sockfd]->Events());
    }
    void RemoveConnection(int sockfd)
    {
        if (!IsConnectionExists(sockfd))
            return;
        // 1. 清理connection自己关心的事件
        _connections[sockfd]->SetEvents(0);
        // 2. 清理epoller中对于sockfd的关心, 写透到内核
        _epoller.DelEvent(sockfd);
        // 3. 移除在_connections中的key value
        _connections.erase(sockfd);
    }
    void LoopOnce(int timeout) // 事件派发
    {
        int n = _epoller.Wait(_revs, gmaxevents, timeout);
        for (int i = 0; i < n; i++)
        {
            int sockfd = _revs[i].data.fd;
            uint32_t events = _revs[i].events;

            // 将所有的epoll检测到的fd的问题,最后统一转换成为读写错误
            if (events & EPOLLERR)
                events |= (EPOLLIN | EPOLLOUT);
            if (events & EPOLLHUP)
                events |= (EPOLLIN | EPOLLOUT);
            // 未来所有的IO操作,都会转化成为下面的部分
            // 整个代码有没有执行IO代码?没有!
            // IO代码将来在哪里???connection的回调中.
            // 我们在干什么???事件派发
            if ((events & EPOLLIN) && IsConnectionExists(sockfd)) // 细节
            {
                if (_connections[sockfd]->_recver)
                    _connections[sockfd]->_recver(_connections[sockfd]);
            }
            if ((events & EPOLLOUT) && IsConnectionExists(sockfd))
            {
                if (_connections[sockfd]->_sender)
                    _connections[sockfd]->_sender(_connections[sockfd]);
            }
        }
    }
    void Dispatcher() // 事件派发器
    {
        _isrunning = true;
        while (_isrunning)
        {
            _epoller.DebugFdList();
            LoopOnce(_timeout); // 事件派发
            // 对其他事情进行处理
        }
        _isrunning = false;
    }
    ~TcpServer()
    {
    }

private:
    Epoller _epoller;
    struct epoll_event _revs[gmaxevents];
    int _timeout;
    std::unordered_map<int, Connection *> _connections;
    bool _isrunning;

    // std::vector<func_t> others;
};

二、OneThreadOneLoop多进程⽅案

One Thread One Loop(OTOL)是⼀种设计模式,通常⽤于描述基于事件驱动编程(EventDriven Programming)的架构,特别是在使⽤异步I/O(Asynchronous I/O)框架时.这种模式强调每个线程运⾏⼀个独⽴的事件循环(Event Loop),从⽽实现⾼效的并发处理.

核⼼概念

1.事件循环(Event Loop)

  • 事件循环是异步编程的核⼼,负责监听事件(如I/O操作、定时器事件等)并触发相应的回调函数.
  • 它通常是⼀个单线程的执⾏模型,通过多路复⽤技术(如 epoll 、 select 或poll)⾼效地管理多个I/O操作.

2.单线程模型

  • 在 One Thread One Loop 模式中,每个线程运⾏⼀个独⽴的事件循环.
  • 这种设计避免了多线程编程中的复杂同步问题,同时利⽤了现代操作系统⾼效的I/O多路复⽤机制.
  • 只要把单Reactor写完,扩展多进程很容易.
  • 每个进程管理的连接和fd,彼此不要重复.
  • 每个Reacor,⾃⼰处理⾃⼰的sockfd的完整⽣命周期,不涉及任何IO穿插,乱序问题.

2.1OneThreadOneLoop多线程⽅案1---主从Reactor多线程模型

注意:管道也能线程间通信,所以我们可以使⽤同样的⽅案进⾏设计.

cpp 复制代码
#include <iostream>
#include <thread>
#include <unistd.h>
#include <cstring>
// 定义管道的读写端
int pipefds[2];
// ⼯作线程函数
void worker_thread() {
char buffer[256];
int n;
while (true) {
n = read(pipefds[0], buffer, sizeof(buffer) - 1);
if (n > 0) {
buffer[n] = '\0'; // 确保字符串结尾
std::cout << "Worker thread received: " << buffer << std::endl;
} else if (n == 0) {
std::cout << "No more data to read. Exiting worker thread." <<
std::endl;
break;
} else {
std::cerr << "Error reading from pipe." << std::endl;
break;
}
}
}
int main() {
// 创建管道
if (pipe(pipefds) == -1) {
perror("pipe");
return 1;
}
// 创建⼯作线程
std::thread worker(worker_thread);
// 主线程向管道写⼊数据
const char* messages[] = {"Hello from main thread!", "Another message.",
"Goodbye!"};
for (const char* msg : messages) {
sleep(1);
if (write(pipefds[1], msg, strlen(msg)) < 0) {
perror("write");
break;
}
sleep(1);
std::cout << "Main thread sent: " << msg << std::endl;
}
// 等待⼯作线程结束
worker.join();
// 关闭管道(可选)
close(pipefds[0]);
close(pipefds[1]);
return 0;
}

主线程负责监听和接收客户端连接,然后通过Pipe(管道)把连接分发给多个工作线程,每个工作线程拥有独立的 Reactor/EventLoop,负责处理自己管理的连接.

它属于一种 主从Reactor多线程模型(Main Reactor + Sub Reactors),也可以理解为单Acceptor + 多Worker EventLoop的网络服务器架构.

1.方案1的整体架构

复制代码
                    客户端连接
                        │
                        ▼
              ┌──────────────────┐
              │   Master 主线程   │
              │ listenSocket     │
              │ Reactor + accept │
              └────────┬─────────┘
                       │
                 新连接 socket fd
                       │
                 选择工作线程
                       │
           ┌───────────┼───────────┐
           │           │           │
           ▼           ▼           ▼
        Pipe 1       Pipe 2      Pipe 3
           │           │           │
           ▼           ▼           ▼
     ┌──────────┐ ┌──────────┐ ┌──────────┐
     │ Worker 1 │ │ Worker 2 │ │ Worker 3 │
     │ Reactor 1│ │ Reactor 2│ │ Reactor 3│
     │ EventLoop│ │ EventLoop│ │ EventLoop│
     └──────────┘ └──────────┘ └──────────┘
           │           │           │
           ▼           ▼           ▼
       处理连接1    处理连接2    处理连接3
       read/write   read/write   read/write
       业务逻辑     业务逻辑     业务逻辑

2.具体工作流程

假设一个客户端连接服务器:

  1. Master 监听: 主线程的 Reactor 监听 listenSocket,发现有新连接.
  2. Master 接收: 主线程调用 accept(),得到客户端的 socket fd,例如 fd=10.
  3. 选择 Worker: 主线程通过轮询算法(Round Robin)选择 Worker 2.
  4. Pipe 通知: 主线程把 fd=10 写入 Worker 2 对应的 Pipe.
  5. Worker 接管: Worker 2 的 EventLoop 监听到 Pipe 可读,读取 fd=10,创建 Connection 对象,并把连接注册到自己的 Reactor.
  6. 处理业务: 以后这个连接的 read、write 和业务回调主要由 Worker 2 负责,Master 不再处理该连接的数据收发.

3.为什么叫 OneThreadOneLoop?

因为它遵循一个核心原则:

每一个 I/O 工作线程拥有一个独立的 EventLoop.

线程 EventLoop 职责
Master Main Reactor 监听、accept、连接分发
Worker 1 Reactor 1 负责分配给自己的连接
Worker 2 Reactor 2 负责分配给自己的连接
Worker 3 Reactor 3 负责分配给自己的连接

多个 Worker 可以在不同 CPU 核心上并行运行,而每个 Worker 内部通过 I/O 多路复用处理多个连接事件.

4.方案1最核心的特点

它将"接收连接"和"处理连接"分离.

  • Master 只做新连接接入和分配.
  • Worker 专门负责客户端连接的 I/O 和业务处理.
  • 每个 Worker 有自己的 Reactor,减少多个线程同时操作同一连接的情况.
  • Master 与 Worker 通过 Pipe 通信,存在一定的线程间通知开销.

这里还有一个容易混淆的点:

Worker 继承 listenSocket,不代表 Worker 也会调用 accept().

5.它与单线程 Reactor 有什么区别?

对比维度 单线程 Reactor OneThreadOneLoop 方案1
EventLoop 1 个 Master 1 个 + Worker N 个
accept 主线程 Master
连接 I/O 单线程处理 多个 Worker 并行处理
业务逻辑 单线程执行 不同 Worker 可并行执行
线程通信 一般不需要 Pipe 通知
性能瓶颈 单线程处理能力 Master 接入能力、Worker 负载等

2.2Eventfd事件驱动

1.什么是 eventfd 事件驱动?

eventfd 是 Linux 提供的一种基于文件描述符的事件通知机制.

它的核心作用是:

一个线程发送通知,另一个线程通过epoll感知事件,从而被唤醒并执行相应任务.

可以把它理解为一个"电子门铃":

  • Master 线程: 有新连接时,向 Worker 的 eventfd 写入数字 1,相当于按门铃.
  • Worker 线程: 使用 epoll_wait() 等待事件,门铃响了就被唤醒.
  • Worker 处理: 读取 eventfd,然后从共享任务队列中取出新连接,注册到自己的 Reactor.

这里有一个关键点:

eventfd 负责通知,不负责传输 socket fd.如果需要把连接交给Worker,通常还需要一个线程安全队列来保存新连接的 fd.

2.在OneThreadOneLoop中的架构

每个 Worker 都有自己的 eventfd 和 epoll,既能监听客户端连接的读写事件,也能监听主线程发来的唤醒通知.

3.eventfd 为什么能唤醒 epoll?

eventfd 内部维护一个 64 位无符号计数器.默认情况下,它的行为如下:

当计数器大于 0,eventfd 就处于可读状态,因此注册了它的 epoll 能报告 EPOLLIN 事件.

4.C++ 实现思路

c 复制代码
// 初始化:每个 Worker 一个 eventfd
int wakeupFd = eventfd(
    0, EFD_NONBLOCK | EFD_CLOEXEC
);

// Worker 将 wakeupFd 注册到自己的 epoll
epoll_event ev{};
ev.events = EPOLLIN;
ev.data.fd = wakeupFd;
epoll_ctl(epollFd, EPOLL_CTL_ADD, wakeupFd, &ev);

// Master:接收到新连接后
worker.queue.push(clientFd);  // 线程安全队列
eventfd_write(worker.wakeupFd, 1);

// Worker:事件循环
while (running) {
    int n = epoll_wait(epollFd, events, MAX_EVENTS, -1);

    for (int i = 0; i < n; ++i) {
        if (events[i].data.fd == wakeupFd) {
            eventfd_t count;
            eventfd_read(wakeupFd, &count);

            // 取出任务,创建 Connection
            // 注册客户端 fd 到当前 EventLoop
            processPendingConnections();
        } else {
            // 处理客户端 read/write 事件
            handleConnectionEvent(events[i]);
        }
    }
}

这是简化后的核心逻辑,省略了线程安全队列实现、错误处理和资源清理.实际实现中还应注意入队与通知的顺序、通知失败处理,以及批量读取任务.

5.Pipe与eventfd的区别

对比 Pipe eventfd
主要用途 传输字节数据 事件通知与计数
文件描述符 通常需要 2 个 1 个
可以传输 socket fd 整数值 可以 不能直接传输
支持 epoll 支持 支持
线程间通知 可以 更有针对性
额外数据结构 不一定需要 通常配合任务队列

2.3Eventfd⼯作模式

Linux 的 eventfd 主要有 两种工作模式:普通计数器模式(默认模式)和信号量模式(EFD_SEMAPHORE).

两种模式的核心区别是:读取事件时,是一次性清空计数器,还是每次只消耗一个计数.

1.两种工作模式

1.普通计数器模式

创建方式:

c 复制代码
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);

假设连续写入三次:

c 复制代码
eventfd_write(efd, 1);
eventfd_write(efd, 1);
eventfd_write(efd, 1);

此时内部计数器为 3.

执行:

c 复制代码
eventfd_t value;
eventfd_read(efd, &value);

读取结果 value = 3,计数器直接变为 0.

2.信号量模式

创建方式:

c 复制代码
int efd = eventfd(
    0,
    EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC
);

同样写入三次 1 后,连续读取的变化:

操作 读取结果 剩余计数器
初始 --- 3
第一次 read 1 2
第二次 read 1 1
第三次 read 1 0
第四次 read EAGAIN 0

其中第四次返回 EAGAIN,是因为设置了 EFD_NONBLOCK.

2.交互模拟:eventfd 读写过程

3.阻塞与非阻塞模式

这和前面两种计数器模式是两个独立的维度.

场景 阻塞模式 非阻塞模式
计数器为 0 时 read 等待计数器变为非零 返回 EAGAIN
写入导致计数器超出最大值 等待可以写入 返回 EAGAIN
常见用途 直接等待事件 配合 epoll 事件循环

另外,epoll 的LT(水平触发)和ET(边缘触发)是事件就绪通知策略,与 eventfd 的普通/信号量模式不是同一回事.

4.OneThreadOneLoop 应该使用哪一种?

推荐:普通计数器模式 + EFD_NONBLOCK + epoll + 线程安全任务队列.

主从 Reactor 多线程架构通常是:

复制代码
Master 接收到新连接
         │
         ▼
将 socket fd 放入 Worker 任务队列
         │
         ▼
eventfd_write(efd, 1)
         │
         ▼
Worker 的 epoll_wait 被唤醒
         │
         ▼
eventfd_read() 清空通知计数
         │
         ▼
取出任务队列中的连接
         │
         ▼
注册到 Worker 自己的 EventLoop
         │
         ▼
处理客户端 read / write 事件

普通模式更适合这里的原因是:Worker 不需要按通知次数逐个处理 eventfd,只需被唤醒一次,然后批量处理队列里的全部待处理任务.


2.4OneThreadOneLoop多线程⽅案2---线程安全任务队列+eventfd事件通知机制

主从Reactor多线程模型+线程安全任务队列(Queue + Mutex)+eventfd事件通知机制.

与前面的方案1相比,最大的变化是:

  • 方案1: Master 通过 Pipe 将 socket fd 传递给 Worker.
  • 方案2: Master 通过 Queue 传递 socket fd,再通过 eventfd 通知 Worker 来取.

它是一种将连接传递与线程唤醒分离的设计.

1.方案2整体架构

复制代码
                  客户端连接
                      │
                      ▼
            ┌────────────────────┐
            │   Master 主线程     │
            │ listenSocket       │
            │ Reactor + accept() │
            └─────────┬──────────┘
                      │
                获取 socket fd
                      │
                      ▼
             选择目标 Worker
                      │
          ┌───────────┼───────────┐
          │           │           │
          ▼           ▼           ▼
      Queue 1      Queue 2      Queue 3
       + Mutex      + Mutex      + Mutex
          │           │           │
          │           │           │
       eventfd 1   eventfd 2   eventfd 3
          │           │           │
          ▼           ▼           ▼
      Worker 1     Worker 2     Worker 3
      Reactor 1    Reactor 2    Reactor 3
      EventLoop 1  EventLoop 2  EventLoop 3
          │           │           │
          ▼           ▼           ▼
        取出 fd     取出 fd     取出 fd
        注册 epoll   注册 epoll   注册 epoll
          │           │           │
          ▼           ▼           ▼
      read/write   read/write   read/write

这里画的是每个 Worker 一个队列的推荐实现.顶部画了一个公共 fd 队列,那也是可行的变体,后面会说明区别.

2.方案2的五个工作步骤

以客户端连接 fd=10 为例.

第一步:Master 接收新连接

Master 使用 epoll 监听 listenSocket,检测到新连接后调用 accept(),得到客户端 fd=10.

第二步:将 fd 放入任务队列

Master 按轮询等策略选择 Worker 2,获取对应队列的 Mutex,将 fd=10 放入队列,随后释放锁.

c 复制代码
lock(mutex);
queue.push(10);
unlock(mutex);

第三步:eventfd 通知 Worker

Master 向 Worker 2 的 eventfd 写入 1:

c 复制代码
eventfd_write(worker2.eventfd, 1);

Worker 2 的 Reactor 已经通过 epoll 监听这个 eventfd,因此可以感知到通知.

第四步:Worker 被唤醒并取出 fd

Worker 2 的 epoll_wait() 返回,执行 eventfd 的可读回调,读取 eventfd 计数器,然后加锁取出队列中的待处理 fd.

c 复制代码
eventfd_read(eventfd, &value);

lock(mutex);
// 批量取出队列里的 fd
unlock(mutex);

第五步:注册到 Worker 的 Reactor

Worker 2 为 fd=10 创建 Connection 和相应的事件监听对象,将其加入自己的 epoll.

从此,fd=10 的读写事件主要由 Worker 2 的 EventLoop 负责,Master 不再处理其业务数据.

3.为什么要同时使用 Queue、Mutex、eventfd?

这三个组件解决的是不同问题.

组件 主要作用 形象理解
Queue 保存待处理 socket fd 快递仓库
Mutex 保护队列并发访问 仓库门锁
eventfd 通知 Worker 有新任务 门铃
Reactor / epoll 监听与分发事件 工作调度员

核心思想:Queue 传递数据,eventfd 传递通知.

eventfd 本身只维护计数器,不能直接保存 fd=10、fd=11、fd=12 这样的任务列表.

4.方案1与方案2的区别

对比项 方案1(Pipe) 方案2(eventfd)
线程间通信 Pipe Queue + eventfd
fd 传递 写入 Pipe 放入 Queue
唤醒 Worker Pipe 可读 eventfd 可读
数据同步 依靠 Pipe 字节流 Mutex 保护 Queue
任务批量处理 可以 可以
复杂程度 较简单 需要管理队列和同步
核心优势 传输与通知合一 任务存储与通知解耦

这里需要注意:不能简单认为 eventfd 一定比 Pipe 性能更高.eventfd 的通知机制很轻量,但方案2引入了队列和锁,实际性能取决于锁竞争、通知频率、批处理方式和负载.

🚀真正的勇者不是流泪的人,而是含泪奔跑的人!


敬请期待下一篇文章内容


每日心灵鸡汤: 慢慢来,一切都在路上!

人生没有白走的路,每一步都算数.不要因为暂时看不到结果,就怀疑自己的努力.花有花期,人有时运,每个人都有属于自己的成长节奏.你不必事事优秀,也不必时时坚强.累了就休息,迷茫了就调整方向,但别轻易否定那个一直努力的自己.生活不会辜负每一个认真前行的人.愿你眼里有光,心中有梦,脚下有路.把今天过好,就是对明天最好的交代.

相关推荐
橙橙笔记1 小时前
具身智能的学习 - 完成一个机械臂 第1节
python·学习·机械臂·具身智能
且从容.3 小时前
ubuntu安装flink
linux·ubuntu·flink
爱和冰阔落5 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨5 小时前
WSL2的安装与使用
linux·wsl
微小的鱼5 小时前
Linux的渲染与显示系统链路
android·java·linux
Hhy_11075 小时前
《C++深度解构06》手撕 string —— 从底层内存管理到深浅拷贝彻底吃透
c语言·开发语言·c++·学习·visual studio
lisanmengmeng5 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
严肃小白兔6 小时前
Cloudflare 更新 API Token 管理:从最小权限到自动化凭据安全
网络·安全·自动化
山甫aa6 小时前
锁一篇搞定:乐观锁、悲观锁、CAS、数据库锁与分布式锁【从零开始的 Web 后端学习】
java·数据库·分布式·后端·学习·web