C++项目:仿muduo库高并发服务器---------LoopThreadPool模块和TcpServer模块的实现


文章目录


前言

本篇文章介绍了LoopThreadPool模块和TcpServer模块的实现其中: LoopThreadPool 模块是用来管理 LoopThread而设计的,帮助我们给新建立的连接进行线程分配。TcpServer 模块用于整合所有相关模块,通过实例化该模块的对象,可简便搭建服务器。


一、LoopThreadPool模块设计

1.1 功能概述

LoopThreadPool 用于管理和分配所有 LoopThread,支持可配置的线程数量 (包括 0 个或多个),并能依据主从 Reactor 模型的逻辑,为新连接分配合适的线程(LoopThread 对应的 EventLoop)以进行事件监控与处理。

  1. 线程数量可配置
    线程池能设置包含 0 个或多个 LoopThread。当从属线程数量为 0 时,实现单 Reactor 服务器:一个线程既负责获取新连接,也负责连接的事件处理。

在一些轻量级场景下,一般不需要多线程执行任务

  1. 管理 LoopThread 对象
    对 0 个或多个 LoopThread 实例进行统一管理,包括创建、维护等操作。
  2. 线程分配功能
    • 当主线程获取新连接后,需将其分配给对应线程的 EventLoop 以进行事件监控和处理。
    • 若从属线程数量为 0,直接将新连接分配给主线程的 EventLoop 处理。
    • 若存在多个从属线程,采用 RR(Round - Robin,轮转) 算法进行线程分配:轮询获取从属线程的 EventLoop,并设置到对应的 Connection 中。

1.2 代码实现

cpp 复制代码
class LoopEventPool{
    public:
    LoopEventPool(EventLoop*loop):_base_loop(loop),_next_index(0),_thread_count(0){}
    //设置线程个数
    void SetThreadCount(int sz){
        _thread_count=sz;
    }
    //创建从属线程
    void Create(){
        if(_thread_count>0){
            _threads.resize(_thread_count);
            _loops.resize(_thread_count);
            for(int i=0;i<_thread_count;i++){
                _threads[i]=new LoopServer();
                _loops[i]=_threads[i]->GetEventLoop();
            }
        }
        return;
    }
    //给新到来的连接获取loop
    EventLoop* NextLoop(){
        if(_thread_count==0){
            return _base_loop;
        }
        _next_index=(_next_index+1)%_thread_count;
        return _loops[_next_index];
    }
    private:
    int _thread_count;//创建从属线程线程个数
    int _next_index;//分配线程索引
    EventLoop* _base_loop;//主线程绑定的loop
    std::vector<LoopServer*> _threads;//丛属线程
    std::vector<EventLoop*> _loops;//从属线程对应的loop
};

二、TcpServer模块设计

2.1 模块组成

  1. Acceptor 对象:用于创建监听套接字。
  2. EventLoop 对象(baseloop):实现对监听套接字的事件监控。
  3. std::unordered_map<uint64_t, PtrConnection> _conns:对所有新建连接进行管理。
  4. LoopThreadPool 对象:创建线程池,对新建连接进行事件监控及处理。

2.2 功能概述

  1. 设置从属线程池数量。
  2. 启动服务器。
  3. 设置多种回调函数(连接建立完成、消息、关闭、任意等),用户设置给 TcpServerTcpServer 再设置给获取的新连接。
  4. 选择是否启动非活跃连接超时销毁功能。
  5. 具备添加定时任务功能。

2.3 流程

  1. TcpServer 中实例化 Acceptor 对象和 EventLoop 对象(baseloop)。
  2. Acceptor 挂到 baseloop 上进行事件监控。
  3. Acceptor 对象就绪可读事件时,执行读事件回调函数获取新建连接。
  4. 对新连接,创建 Connection 进行管理。
  5. 对连接对应的 Connection 设置功能回调(连接完成回调、消息回调、关闭回调、任意事件回调)。
  6. 启动 Connection 的非活跃连接的超时销毁规则。
  7. 将新连接对应的 Connection 挂到 LoopThreadPool 中的从属线程对应的 EventLoop 中进行事件监控。
  8. 一旦 Connection 对应的连接就绪了可读事件,则执行读事件回调函数,读取数据,读取完毕后调用 TcpServer 设置的消息回调。

2.4 代码实现

cpp 复制代码
//对所有模块管理、快速搭建服务区
class TcpServer{
        //由组件使用者设置的事件回调
    using Functor=std::function<void()>;
    using ConnectedCallback=std::function<void(PtrConnection)>;
    using MessageCallback=std::function<void(PtrConnection,Buffer*buf)>;
    using ClosedCallback=std::function<void(PtrConnection)>;
    using AnyEventCallback=std::function<void(PtrConnection)>;
    ConnectedCallback _connected_callback;
    MessageCallback _message_callback;
    ClosedCallback _closed_callback;
    AnyEventCallback _event_callback;
    ClosedCallback _server_closed_callback;
private:
    //为线连接创建Connection对象
    void NewConnection(int newfd){
        _next_id++;
        PtrConnection conn(new Connection(_threadpool.NextLoop(), _next_id, newfd));
        conn->SetMessageCallback(_message_callback);
        // conn->SetAnyEventCallback(std::bind(HandleRead));
        conn->SetConnectedCallback(_connected_callback);
        conn->SetClosedCallback(_closed_callback);
        conn->SetAnyEventCallback(_event_callback);
        //内部设置的清理连接信息回调
        conn->SetSrvClosedCallback(std::bind(&TcpServer::RemoveConnection,this,std::placeholders::_1));
        _conns.insert({_next_id, conn});
        if(_enable_inactive_release)
        conn->EnableInactiveRelease(_timeout);
        conn->Established();
        DBG_LOG("NEW LINK THREAD");
    }
    //将连接信息从_conns中移除
    void RemoveConnection(const PtrConnection& conn){
        _baseloop.RunInLoop(std::bind(&TcpServer::RemoveConnectionInLooop,this,conn));
    }
    void RemoveConnectionInLooop(const PtrConnection& conn){
        uint64_t id=conn->Id();
        auto it=_conns.find(id);
        if(it!=_conns.end()){
            _conns.erase(id);
        }
    }
    void RunAfterInLoop(const Functor&task,int dalay){
        _next_id++;
        _baseloop.TimerAdd(_next_id,dalay,task);
    }
public:
    TcpServer(int port)
    :_next_id(0)
    ,_port(port)
    ,_enable_inactive_release(false)
    ,_acceptor(&_baseloop,port)
    ,_threadpool(&_baseloop){
        _acceptor.SetAcceptCallback(std::bind(&TcpServer::NewConnection,this,std::placeholders::_1));
    }
    //设置从属线程个数
    void SetThreadCount(int sz){
        _threadpool.SetThreadCount(sz);
    }
    void SetConnectedCallback(const ConnectedCallback&cb) { 
        _connected_callback = cb; 
    }
    void SetMessageCallback(const MessageCallback&cb) { 
        _message_callback = cb; 
    }
    void SetClosedCallback(const ClosedCallback&cb) { 
        _closed_callback = cb; 
    }
    void SetAnyEventCallback(const AnyEventCallback&cb) { 
        _event_callback = cb; 
    }
    void SetSrvClosedCallback(const ClosedCallback&cb) { 
        _server_closed_callback = cb; 
    }
    //启动非活跃销毁
    void EnableInactiveRelease(int timeout){
        _timeout=timeout;
        _enable_inactive_release=true;
    }
    //添加定时任务
    void RunAfter(const Functor&task,int dalay){
        _baseloop.RunInLoop(std::bind(&TcpServer::RunAfterInLoop,this,task,dalay));
    }
    void Start(){
        //创建从属线程
        _threadpool.Create();//创建从属线程
        _acceptor.Listen();//开始监听套接字
        std::cout<<"1"<<std::endl;
        _baseloop.Start();//开始监听处理事件
    }
private:
    uint64_t _next_id;//自增长的线程id;
    int _port;//监听套接字绑定的端口号
    int _timeout;//非活跃连接的超时时间
    bool _enable_inactive_release;//是否启用非活跃连接
    EventLoop _baseloop;//对监听套接字进行事件监控
    Acceptor _acceptor;//创建并管理监听套接字
    LoopEventPool _threadpool;//对从属线程进行管理
    std::unordered_map<uint64_t,PtrConnection> _conns;//存储连接信息

};
相关推荐
daad7771 天前
wifi_note
运维·服务器·数据库
IT界的老黄牛1 天前
Linux 压缩命令实战:tar、gzip、bzip2、xz、zstd 怎么选?一篇讲清楚
linux·运维·服务器
良木生香1 天前
【C++初阶】C++入门相关知识(2):输入输出 & 缺省参数 & 函数重载
开发语言·c++
小此方1 天前
Re:从零开始的 C++ 进阶篇(三)彻底搞懂 C++ 多态:虚函数、虚表与动态绑定的底层原理
c++
计算机毕设vx_bysj68691 天前
【免费领源码】77196基于java的手机银行app管理系统的设计与实现 计算机毕业设计项目推荐上万套实战教程JAVA,node.js,C++、python、大屏数据可视化
java·mysql·智能手机·课程设计
忘梓.1 天前
墨色规则与血色节点:C++红黑树设计与实现探秘
java·开发语言·c++
hhh3u3u3u1 天前
Visual C++ 6.0中文版安装包下载教程及win11安装教程
java·c语言·开发语言·c++·python·c#·vc-1
凤年徐1 天前
C++手撕红黑树:从0到200行,拿下STL map底层核心
c++·后端·算法
星河耀银海1 天前
C++ 模板进阶:特化、萃取与可变参数模板
java·开发语言·c++
cccccc语言我来了1 天前
【C++---unordered_set/map底层封装】个不拘一格的集合。它不似有序集合那般循规蹈矩,而是以一种洒脱不羁的方式,将元素们随意地散落其中。每一个元素都是独一无二的。
开发语言·c++·哈希算法