006 仿muduo实现高性能服务器组件_超时连接管理

​🌈个人主页:Fan_558
🔥 系列专栏:仿muduo
📒代码仓库: 项目代码
🌹关注我💪🏻带你学更多知识

前言

本文的重点是EventLoop模块,从一开始我们说该项目是一个one thread one loop式的主从reactor服务器,这个loop指的就是eventloop,也叫做reactor模块
关于什么是主从reactor与one thread one loop一个线程绑定一个eventloop该如何理解,下一篇都会解答

poller模块

设计意义

描述符IO事件监控模块对epoll的封装,让对描述符进行事件监控的操作更加简单

整体设计

在这里插入图片描述
核心接口(对监控事件的管理:
UpdateEventMonitor添加或修改监控事件 RemoveEventMonitor移除监控 MonitorActiveEvents监视活跃事件
关于MonitorActiveEvents
这个函数的功能是监视活跃事件,并保存活跃事件到数组中返回给上层,当我们添加或删除对于的事件监控的时候,会调用epoll_ctl向epoll实例中添加或删除相关的需要监听的文件描述符及其相关事件,这个相关事件其实就是我们的channel对象,因为我们对于事件的回调设置是在channel模块中的,channel类中包含了_events成员变量,而我们在channel类模块就包含了IsReadable(),IsReadable()函数,因此我们就可以通过_events成员变量得知监控了什么事件,然后进一步可以通过channel类中地HandleReadyEvent函数处理就绪的事件
在这里插入图片描述

TimerWheel模块

设计意义

先前在003前置知识文章中戳此跳转也有提到,这里就一笔带过了
由于服务器的资源是有限的,为了避免某些客户端连接上来之后一直不通信
而平白浪费服务器资源的情况,我们需要对非活跃连接设置定时销毁

整体设计

在这里插入图片描述
在TimeWheel中的构造函数里启动了对_timerfd的可读事件监控,每隔1s就会向_timerfd中写入一个8字节的数据来代表超时的次数,此时可读事件是一直被循环调用的(EventLoop中的Start函数是循环调用的),回调HandleRead函数,而HandleRead的内容就是读取_timerfd中的内容得知实际超时的次数,让_tick指针向后移动对应的超时次数,析构沿途的下标中的定时器对象
在这里插入图片描述

在这里插入图片描述

EventLoop模块

设计意义

最早,我们说要实现的项目目标是:实现一个高性能的服务器,高性能服务器实际上是叫做一个one thread one loop模式的一个主从reactor服务器,这个loop指的就是eventloop,也叫reactor,主从reactor服务器,即主reactor线程只用于监听新连接和分发新连接进⾏通信事件监控,从属reactor线程用于监控各⾃的描述符的读写事件进⾏数据读写以及业务处理,one thread one loop意思就是一个线程绑定一个eventloop

整体设计

EventLoop模块是对Poller模块、TimerWheel、Channel模块的一个整体封装,进行所有描述符的事件监控
在这里插入图片描述poller模块
UpdateEventMonitor添加/修改描述符的事件监控 RemoveEventMonitor移除描述符的监控
TimerWheel 模块
HasTimer是否有某个定时器任务的存在 AddTimer添加定时器任务 TimerRefresh刷新定时器任务 TimerCancel 取消定时器任务 WakeUpEpollWait向_event_fd中写入数据 ReadEventfd从_event_fd中读取数据,防止一直触发事件 CreateEventFd
EventLoop模块的核心接口
RunTaskPool:执行任务池中的所有任务 Start:监控活跃事件、就绪事件处理、执行任务
Start函数分析
Start中包含了MonitorActiveEvents、HandleReadyEvent、RunTaskPool函数
首先监视活跃事件,并保存活跃事件到数组(保存一个个的channel对象)中返回给上层,上层循环调用HandleReadyEvent处理就绪事件(实际上就是调用每个channel对象的HandleReadyEvent函数,最后需要执行任务队列中对连接的操作(在connection模块中我们提到对连接的操作可能会涉及到线程安全问题,因此把对连接的操作压入一个任务池中,只需要给这个任务队列加一把锁就可以保证线程安全,这样能保证对于连接的所有操作,就是在一个线程中进行的,不涉及线程安全问题
如以下对连接的操作都是通过RunInLoop进行的,RunInLoop即看看当前线程是不是EventLoop对应的线程,如果不是就压入任务池里

void Established() {
    _loop->RunInLoop(std::bind(&Connection::EstablishedPriv, this));
}
//发送数据,将数据放到发送缓冲区,启动写事件监控
void Send(const char *data, size_t len) {
    Buffer buf;
    buf.WriteAndPush(data, len);
    _loop->RunInLoop(std::bind(&Connection::SendPriv, this, std::move(buf)));
}
//提供给组件使用者的关闭接口--并不实际关闭,需要判断有没有数据待处理
void Shutdown() {
    _loop->RunInLoop(std::bind(&Connection::ShutdownPriv, this));
}
void Release() {
    _loop->PushInPool(std::bind(&Connection::ReleasePriv, this));
}
//启动非活跃销毁,并定义多长时间无通信就是非活跃,添加定时任务
void StartInacyiveRelease(int sec) {
    _loop->RunInLoop(std::bind(&Connection::StartInacyiveReleasePriv, this, sec));
}
//取消非活跃销毁
void CloseInactiveRelease() {
    _loop->RunInLoop(std::bind(&Connection::CloseInactiveReleasePriv, this));
}
//切换协议---重置上下文以及阶段性回调处理函数 -- 而是这个接口必须在EventLoop线程中立即执行
//防备新的事件触发后,处理的时候,切换任务还没有被执行--会导致数据使用原协议处理了。
void Upgrade(const Any &context, const EstablishedCallBack &conn, const MessageCallBack &msg, 
             const ClosedCallBack &closed, const AnyEventCallBack &event) {
    _loop->AssertInLoop();
    _loop->RunInLoop(std::bind(&Connection::UpgradeInLoop, this, context, conn, msg, closed, event)); 
}

整体代码

在这里插入图片描述

小结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fan_558

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值