✨✨欢迎来到T_X_Parallel的博客!!
🛰️博客主页:T_X_Parallel
🛰️项目代码仓库:Reactor模型高并发服务器项目代码仓库(随博客更新)
🛰️专栏 : Reactor模型高并发服务器项目
🛰️欢迎关注:👍点赞🙌收藏✍️留言
项目环境:vscode、wsl2(Ubuntu22.04)
技术栈:C/C++、C++11、C++17、STL、HTTP、TCP、HTML
1. 项目概述
使用一个高并发组件即可搭建一个高性能服务器,在C++第三方库中的Muduo库就是高性能开源网络库,专为Linux多线程服务端程序设计。该库以事件驱动为核心,采用非阻塞IO和Reactor模式实现高并发处理,内部通过高效的epoll与多线程协作机制优化资源调度。
那么这个项目就是仿照muduo库的实现方式,自己实现一个one thread one loop式主从Reactor模型高并发服务器组件,通过自己实现的高并发服务器组件,就可以简洁快速的完成一个高性能的服务器搭建,同时,实现过程中也能巩固网络通信知识。
由于有多种应用层协议,所以该项目中实现的组件内应提供不同的应用层协议支持(为了便于演示,项目中先只提供HTTP协议组件的支持)
2. 相关概念解释
HTTP服务器
HTTP(HyperTextTransferProtocol)即超文本传输协议,HTTP是应用层协议,是一种简单的请求-响应协议
HTTP协议是一个运行在TCP协议之上的应用层协议,表面HTTP服务器其实就是个TCP服务器,只不过在应用层基于HTTP协议格式进行数据的组织和解析来明确客户端的请求并完成业务处理
HTTP服务器搭建方式简单来说就四步
- 搭建一个TCP服务器,接收客户端请求
- 以HTTP协议格式进行解析请求数据,明确客户端请求目的
- 明确客户端请求目的后提供相对应的服务
- 将服务结果按HTTP协议格式进行组织,发送回客户端
实现一个HTTP服务器很简单,但是要实现一个高性能的服务器并不简单,下面讲解一下高性能服务器的核心模式——Reactor模式
Reactor模型
概念
Reactor模式是指通过一个或多个输入同时传递给服务器进行请求处理时的事件驱动处理模式。服务端程序处理传入的多路请求时,将它们同步分派给请求对应的处理线程,所以Reactor模式也叫做Dispatcher模式。
Reactor模式简单来说就是使用I/O多路复用
统一监听事件,收到事件后再分发给处理进程或者线程,这是实现高性能网络服务器的必备技术之一
分类
- 单Reactor单线程:单线程中I/O多路复用+业务处理
使用I/O多路复用进行客户端请求监控,当触发事件后,进行事件处理,但是此时分两种情况进行处理
- 如果是新建连接请求,则获取新建连接,并添加至多路复用中进行事件监控
- 如果是数据通信请求,则进行数据处理(接收数据、解析数据、发送响应)
优点:所有操作都在同一个线程中完成,流程和实现较为简单,不涉及进程以及线程间通信及资源争抢问题
缺点:无法有效利用CPU多核资源,容易达到性能瓶颈
适用场景:适用于客户端数量较少,且处理速度较快的场景
处理较慢或活跃连接较多,会导致串行处理的情况,后面处理的连接会长时间无法得到响应
- 单Reactor多线程:单I/O多路复用+多线程业务处理(线程池)
Reactor线程通过I/O多路复用进行客户端请求监控,如果事件触发,则进行事件处理,但存在以下三种情况
- 如果是新建连接请求,则获取新建连接,并添加至多路复用中进行事件监控
- 如果是数据通信请求,则通过接收数据后分发给业务处理线程池进行业务处理
- 如果工作线程处理完毕后,则将响应交给Reactor线程进行数据响应
优点:充分利用CPU多核资源
缺点:多线程间的数据共享访问控制较为复杂,单个Reactor将承担所有事件的监听和响应,在单线程中运行,高并发场景下容易成为性能瓶颈
适用场景:适用于需要高并发连接且业务处理逻辑轻量的场景,通过多路复用处理I/O事件与非阻塞读写,配合线程池实现业务逻辑的并行处理,提升吞吐量的同时兼顾多核资源利用
- 多Reactor多线程:多I/O多路复用+多线程业务处理(线程池)
- 在主Reactor中处理新连接请求事件,有新连接到来则分发新连接到子Reactor中进行监控
- 在子Reactor中进行客户端通信监控,如果有事件触发,则接收数据并分发给业务处理线程池
- 业务处理线程池分配独立的线程进行具体的业务处理,处理完毕后,将响应数据交给子Reactor线程进行数据响应
优点:充分利用CPU多核资源。主从Reactor各司其职
缺点:线程调度多,涉及多种线程间问题
适用场景:适用于需要处理高并发、高吞吐量的网络服务的场景,通过主从Reactor分工(主负责连接建立,子负责IO处理)和多线程协作,有效提升并发性能与资源利用率
3. 项目目标定位
从上面的分类来看,该项目目标要实现的就是主从Reactor模型服务器,即主Reactor线程只监听描述符,获取新建连接,保证获取新连接的高效性,提高服务器的并发性能。然后主Reactor线程获取到新连接后分发给子Reactor进行通信事件监控,而子Reactor线程监控各自的描述符的读写事件进行数据读写以及业务处理
One Thread One Loop的思想就是将所有的操作放到一个线程中进行,并且一个线程对应一个事件处理的循环
项目实现中,由于不确定组件使用者的使用意向,因此并不提供工作 线程池中的实现,只实现主从Reactor,而工作线程池可由组件使用者自行根据需求决定是否使用和实现
4. 项目功能模块划分
基于上面的相关内容介绍,项目所要实现的就是一个带协议支持的Reactor模型高性能服务器,则可以将整个项目的实现划分为两个大模块
- SERVER模块:实现Reactor模型的TCP服务器
- 协议模块:对当前的Reactor模型服务器提供应用层协议支持
SERVER模块
SERVER模块就是对所有的连接以及线程进行管理,管理线程在合适的事件做合适的事情
具体的管理分为以下三个方面
- 监听连接管理
- 通信连接管理
- 超时连接管理
基于上面的管理的三个方向,将对SERVER模块进一步划分可以划分为以下9个模块
Buffer模块
该模块是一个缓冲区模块,用于实现通信中用户的接收缓冲区和发送缓冲区功能
Socket模块
该模块是对套接字操作封装的一个模块,主要实现socket的各种操作
Channel模块
该模块是对一个描述符需要进行的IO事件管理的模块,实现对描述符可写、可读、错误等事件的管理操作,以及Poller模块对描述符进行IO事件监控就绪后,根据不同的事件,回调不同的处理函数功能
简单来说就是回调函数的存储对象
Connection模块
该模块是对Buffer模块、Socket模块和Channel模块的一个整体封装,实现了对一个通信套接字的整体管理,即每一个进行数据通信的套接字(accept获取到的新连接)都会使用Connection管理
Connection模块的大致实现内容
- 包括有三个由组件使用者传入的回调函数——连接建立完成回调、事件回调、新数据回调、关闭回调
- 包含由组件使用者听过的两个接口:数据发送接口、连接关闭接口
- 包含两个用户态缓冲区:用户态接收缓冲区、用户态发送缓冲区
- 包含一个Socket对象,完成描述符面向系统的IO操作
- 包含一个Channel对象,完成描述符IO事件就绪的处理
Connection模块内的大致处理流程
- 实现向Channel对象提供可读、可写以及错误等不同事件的IO事件回调函数,然后将Channel对象和对应的描述符添加到Poller事件监控中
- 当描述符在Poller模块中就绪了IO可读事件,则调用描述符对应的Channel对象中保存的读事件处理函数,进行数据读取,再将socket接收缓冲区全部读取到Connection管理的用户态接收缓冲区张,然后调用组件使用者传入的新数据到来对应的回调函数进行处理
- 组件使用者进行数据的业务处理完毕后,通过Connection向使用者提供的数据发送接口,将数据写入Connection的发送缓冲区中
- 启动描述符在Poller模块中的IO写事件监控,当就绪后,调用Channel中保存的写事件处理函数,将发送缓冲区中的数据通过Socket进行面向系统的实际数据发送
Acceptor模块
该模块是对Socket模块和Channel模块的一个整体封装,实现了对一个套接字的整体管理
Acceptor模块的大致实现内容
- 包含一个Socket对象,实现监听套接字的操作
- 包含一个Channel对象,实现监听套接字IO事件就绪的操作
Acceptor模块内的大致处理流程
- 实现向Channel对象提供可读事件的IO事件处理回调函数,函数功能为获取新连接
- 为新连接构建一个Connection对象
TimerQueue模块
该模块主要是执行定时任务的模块,也就是定时任务的管理器,向该管理器中添加任务,任务将会在固定时间后被执行,同时也可以通过刷新操作来延迟任务的执行、取消操作来取消任务的执行
这个模块主要对每一个Connection对象的生命周期管理,对非活跃连接超时后的释放功能
TimerQueue模块大概实现内容
- 包含一个定时器:具体实现请看下一节内容(项目准备)
- 包含一个Channel对象,实现对定时器的时间就绪的回调操作
Poller模块
该模块是对epoll进行封装的一个模块,主要实现epoll的IO事件添加、修改、移除以及获取活跃连接功能
EventLoop模块
该模块可以理解为上面说的子Reactor模块,它是对Poller模块、TimerQueue模块和Socket模块的一个整体封装,进行对所有描述符的事件监控,所以EventLoop模块必然是一个对象对应一个线程的模块,线程内容的目的就是运行EventLoop的启动函数
该模块为了保证整个服务器的线程安全问题,因此要求使用者对于Connection对象的所有操作一定要在其对应的EventLoop线程中完成,不能再其他线程中进行
该模块保证自己内部所监听的所有描述符都要是活跃连接的,非活跃连接就要及时释放避免资源浪费
EventLoop模块大致实现内容
- 包含一个事件描述符eventfd(eventfd是linux内核提供的一个事件fd,专门用于事件通知)
- 包含一个Poller对象,用于进行描述符的IO事件监控
- 包含一个TimerQueue对象,用于进行定时任务的管理
- 包含一个PendingTask队列,组件使用者对Connection进行的所有操作都加入到任务队列中,由EventLoop模块进行管理,并在EventLoop对应的线程中进行执行
- 每一个Connection对象都会绑定到一个EventLoop对象中,这样就能保证对这个连接的所有操作都是在一个线程中完成的
EventLoop模块内的大致处理流程
- 通过Poller模块对当前模块管理的所有描述符进行IO事件监控,当描述符有事件就绪后,通过描述符对应的Channel对象进行事件处理
- 所有就绪的描述符IO事件处理完成后,对任务队列中的所有任务进行顺序执行
- 由于epoll事件监控,有可能因为没有事件到来而持续阻塞,导致任务队列中的任务不能及时执行,因此创建eventfd,添加到Poller的事件监控中,用于实现每次向任务队列添加任务时,通过向eventfd写入数据来唤醒epoll的阻塞
TcpServer模块
该模块时一个整体Tcp服务器模块的封装,内部封装了Acceptor模块、EventLoop模块
TcpServer模块大致实现内容
- 包含一个EventLoop对象,以备在超轻量使用场景不需要EventLoop线程池,只需要在主线程中完成所有操作的情况
- 包含一个EventLoopThreadPool对象,即EventLoop线程池,也为子Reactor线程池
- 包含一个Acceptor对象,一个TcpServer服务器,必然对应有一个监听套接字,能够完成获取客户端新连接并处理的任务
- 包含一个
shared_ptr<Connection>
的哈希表,该哈希表保存了所有新连接对应的Connection对象(注:所有Connection对象应该使用shared_ptr进行管理,这样能够保证在哈希表中删除了Connection信息后,当shared_ptr计数器为0的情况下完成对Connection对象资源的释放)TcpServer模块内的大致处理流程
- 在实例化TcpServer对象过程中,完成BaseLoop的设置、Acceptor对象的实例化以及EventLoop线程池的实例化,还有
shared_ptr<Connection>
的哈希表的实例化- 获取新连接后,为新连接构建Connection对象,设置各种回调,并使用shared_ptr智能指针进行管理,同时添加到哈希表中,并为Connection对象选择一个EventLoop线程,同时为Connection对象添加定时任务和事件监控
- 启动BaseLoop
该部分模块划分比较多且复杂,博主建议跟着博主把后面的具体代码实现完后,结合代码再将这部分的内容和具体关系再重新梳理一遍,那时应该就能清晰了
HTTP协议模块
HTTP协议模块用于对高并发服务器模块进行协议支持,基于提供的协议支持能够更方便的完成指定协议服务器的搭建
HTTP协议支持模块的实现可分为以下5个模块
Util模块
这个模块时一个工具模块,主要提供HTTP协议模块所用到的一些工具函数,比如URL编解码、文件读写等
HttpRequest模块
这个模块时HTTP请求数据模块,用于保存HTTP请求数据被解析后的各项请求元素信息
HttpResponse模块
这个模块时HTTP响应数据模块,用于业务处理后设置并保存HTTP响应数据的各项元素,最终会被按照HTTP协议响应格式构建成响应信息响应给客户端
HttpContext模块
这个模块是HTTP请求接收的上下文模块,主要为了防止某次接收的数据中因某种原因导致请求不完整,则解析过程并未完成,无法进行完整的请求处理,需要在下次接收到新数据后继续根据上下文进行解析,最终得到一个HttpRequest请求信息对象,因此在请求数据的接收以及解析部分需要一个上下文来进行控制接收
HttpServer模块
这个模块就是给组件使用者提供的HTTP服务器模块,用于以简单的接口实现HTTP服务器的搭建
HttpServer模块大致实现内容
- 包含一个TcpServer对象,实现服务器的搭建
- 包含提供给TcpServer对象的两个接口:连接建立成功设置上下文接口、数据处理接口
- 包含一个哈希表存储请求与处理函数的映射表,组件使用者向HttpServer设置哪些请求应该使用哪些函数进行处理,等TcpServer收到对应的请求就会使用对应函数进行处理
下一个内容就是做一些项目的准备工作,敬请期待
专栏:Reactor模型高并发服务器项目
项目代码仓库:Reactor模型高并发服务器项目代码仓库(随博客更新)
都看到这里了,留下你们的珍贵的👍点赞+⭐收藏+📋评论吧