muduo库的模拟实现——muduo库的介绍

文章目录

  • 一、muduo库介绍
  • 二、背景知识
    • 1.epoll
    • 2.Reactor模式
  • 三、功能模块划分
    • 1.工具部分
    • 2.Reactor部分
    • 3.TCPServer部分

一、muduo库介绍

muduo库是在Linux环境下使用C++实现的一个多Reactor多线程的高性能网络服务器,作者陈硕,他还出了一本书《Linux多线程服务端编程:使用muduo C++网络库》来介绍muduo库的使用以及设计。有兴趣的读者可以阅读一下书中关于muduo库的设计部分,本篇文章就是基于这本书来介绍如何模拟实现一个muduo网络库。因为我认为学习技术,有了初步的了解以后就要想办法复刻模仿,学习别人的设计思路,复刻的过程就是动手实践,只有在实践中我们才能真正感受到难点在哪,以及优秀的地方在哪,这对我们学习相关知识比如计算机网络、IO多路转接、Reactor模式是很有帮助的。

比如在模拟实现muduo库的过程中,我最大的感受就是这个库很难,至少对于我这么一个刚学习完计算机网络、多路转接的学生来说是有挑战的,代码中使用了大量的回调函数,以及多线程并发执行的逻辑,我刚开始看的时候是很绕很乱的,理不清这些关系。但模拟实现完成以后,现在再回头看这些代码,确实是非常优秀的设计。本篇文章的目的是记录这几个月学习并模拟实现muduo库的过程,分享一下我个人对muduo库的认识,如果有不正确的地方欢迎指正。

二、背景知识

muduo库是基于Reactor模式下使用epoll多路转接的方式设计出来的,所以在模拟实现moduo库之前,非常有必要补充一下这方面的背景知识。

1.epoll

epoll是多路转接中使用最多并且是最高效的方式,多路转接又叫多路复用,其实我认为多路复用比较形象好理解,多路复用是用来解决一个服务器如何更好地服务多个用户的问题。试想一下,如果我们写的服务器只能服务一个用户,那么效率太低了,而且也很浪费资源。

那如果要处理多个客户端的请求,首先想到的是多进程模型,也就是每来一个用户,服务器就创建一个新进程来为用户服务,但这种方式有明显的劣势,因为进程的开销太大了。如果进程不行的话,就有多线程模型,每来一个用户就创建一个线程,或者使用线程池的方式,可以避免频繁创建和销毁线程。但多线程也只能解决用户少的情况,如果用户量很大,连接数很多,一个服务器要维护成千上万个线程,其实开销也是特别大的,即使线程比进程更轻量级。

IO多路复用就和多进程模型、多线程模型不一样,因为一个服务就分配一个线程或者进程开销太大,所以IO多路复用是让多个事件复用同一个进程。用户与服务器的交互无非就是建立连接请求、发送IO请求,这些请求在服务器层面看来,本质都是一个个的事件,建立连接事件以及IO处理事件,IO多路复用是让一个进程管理多个事件,其实更进一步说明应该是,进程调用操作系统提供的多路复用的接口,比如select、poll和epoll,通过这些接口进程将需要管理的事件交给操作系统内核去监听,一旦有事件就绪,操作系统内核会以不同的方式通知进程,进程再去做相应的处理,这就是IO多路复用的原理。

epoll就是操作系统为我们提供的IO多路复用接口,在使用epoll多路复用时,操作系统会为我们创建一个epoll模型,这个模型由三部分组成:红黑树、就绪队列以及回调机制。epoll的底层实现原理是当进程向epoll模型输入需要管理的事件时,epoll模型会创建一个相应的红黑树节点,将该事件记录在红黑树上。然后操作系统会为红黑树上的每一个事件注册一个回调函数,当事件就绪时,红黑树上的事件节点会被删除,然后向就绪队列中插入一个新的节点,通过回调函数告诉上层的进程某某事件已经就绪了,就绪事件就放在就绪队列里,上层进程就可以对就绪事件进行相应的进一步处理了。这就是epoll模型的基本原理。

在这里插入图片描述

这里只是作为背景知识简单地介绍了一下epoll模型,详细的可以看我之前写过的博客:Linux多路转接之epoll

2.Reactor模式

Reactor模式是对多路复用的进一步设计,如果单纯使用epoll的多路复用,进程调用epoll接口监听事件,如果有事件就绪还是由这个进程来执行就绪事件的对应操作。也就是说单纯的多路复用是将事件监听和就绪事件的处理合在一起的,而Reactor模式就是将它们分开来处理。Reactor模式又叫做dispatcher模式,dispatcher有分派的意思,其实是比较形象地形容了Reactor模式的,因为Reactor模式中一般可以分出两类角色,一类是Reactor角色,一类是handler角色,其中Reactor角色负责的是监听和分发事件,handler角色负责的是处理就绪事件。Reactor角色会等待多路复用返回就绪事件,一旦被通知有事件就绪,Reactor角色就会把就绪的事件交给handler角色去处理,然后Reactor角色就可以继续等待下一轮的就绪事件,这就是Reactor模式的原理。

Reactor的模式是灵活多变的,在不同的业务场景下,我们可以选择单个或者多个reactor角色,同时也可以选择单个或多个handler角色。其实handler角色通常情况下就是额外的进程和线程,因为Reactor角色接收到就绪事件以后肯定是分派给其它执行进程或者线程去处理就绪事件。所以Reactor模式又可以分为单Reactor单进程/线程单Reactor多进程/线程多Reactor单进程/线程多Reactor多进程/线程4种方案。

由于muduo库使用的就是多Reactor多线程的方案,所以这里只介绍这种方案,其实这种方案听起来很复杂,但我个人认为是最好理解、最好实现的方案,因为它能做到分工明确。首先来看一下多Reactor多线程的模型图:

在这里插入图片描述

通过模型图可以看到,多Reactor多线程模型分了一个主Reactor和多个从属Reactor,当然也有一个主线程和多个子线程。它的执行逻辑是,主线程的主Reactor通过epoll监听连接事件,并且主Reactor只监听连接事件,当连接事件到来的时候,主线程获取连接,获取到的连接又是一个新的文件描述符,也就是一个新的事件,这个事件可能还会有新的IO事件,所以也必须被管理起来。这些新连接就被主Reactor分派给某个子线程的从属Reactor去管理。同样的,从属Reactor就负责监听这些连接事件的IO事件,当这些连接有IO请求的时候,就让Handler去处理这些请求。这就是多Reactor多线程模型的执行逻辑。

多Reactor多线程模型在muduo库中的体现是,首先会有一个主Reactor,它只负责监听连接事件,然后会维护一个线程池,线程池里的线程可以指定数量,当主Reactor监听到连接事件以后,就选定线程池里的一个线程,将该事件的文件描述符与子线程的从属Reactor绑定,子线程的从属Reactor就负责监听该文件描述符的IO事件,而主Reactor就继续去监听等待新的连接事件到来。这样分工是非常明确的,也是非常好理解。

三、功能模块划分

介绍完两个重点的背景知识以后,我们可以对自己模拟实现的muduo库做一个功能划分,这里首先划分TCP服务器层的功能模块,因为这才是整个网络库的核心,所有的多Reactor多线程模型,所有的高性能的实现都是在TCP服务器里面体现的,这一层做好了,上层应用层选择需要的协议就可以了,比如HTTP协议,搭建一个HTTP协议并不是什么难点,所以放在最后再来说,重点还是放在TCP服务器上。

TCP服务器的功能模块我个人认为可以划分出三个部分,分别是工具部分、Reactor部分和最上层的TCPServer部分。工具部分有Buffer模块、Socket模块、Acceptor模块、定时器模块、线程池模块。之所以这些模块被我划分为工具部分,是因为我认为在这个项目中这些模块更多的是起到一个工具的辅助作用,最重点的还是Reactor部分。Reactor部分有Channel模块、Poller模块、Connection模块和EventLoop模块,这部分的四个模块,就是实现多Reactor多线程、完成连接管理的模块,我认为是项目的核心模块。最上层的TcpServer模块当然就是将这些各个模块整合起来,形成一个类或者接口,提供给外部调用。下面将分别介绍每个模块的大致功能,后面会详细介绍每一个模块的原理、作用以及具体实现。

1.工具部分

Buffer模块:
Buffer模块是TCP服务器的缓冲区模块,缓冲区这个概念在计算机里是非常常见的,我们平时使用的软件比如操作系统、数据库,以及一些第三方库、组件等等,都有缓冲区的存在。缓冲区就是一段缓存数据的内存空间,在我们这个TCP服务器中,我们需要接收缓冲区来保存对方发送的请求报文,也需要发送缓冲区保存我们需要发送给对方的响应报文。所以Buffer模块就是为我们的TCP服务器维护了一段缓冲区。

Socket模块:
这个模块比较简单,就是对TCP服务器中要使用到的套接字接口进行封装,因为要符合面向对象的思想,让开发调用更方便,也为了让代码更美观和结构化,所以很有必要将socket套接字操作封装成一个单独的模块。

Acceptor模块:
Acceptor模块会跟主Reactor有比较大的联系,因为Acceptor模块是负责监听事件管理的模块,它通过Socket模块实现监听套接字的操作,创建了监听套接字以后,它就会accept监听获取连接,当有连接到来的时候,它就会通知主Reactor去处理连接。

定时器模块:
我们知道网络连接有长连接和短连接,我们的服务器也是可以接收长连接和短连接的,但是长连接不能占用套接字的时间太长,如果一个连接到来但一直没有与服务器有其它IO数据交互,就只是占用着连接不销毁,这其实是非常消耗服务器资源的,虽然一个连接长时间占用资源看起来影响不大,但如果有成千上万个连接到来,服务器的资源是有限的,就比如文件描述符的资源是有限的,如果有长连接长时间不通信还占用着文件描述符,那么如果文件描述符都被占满了,新的连接就无法到来了,所以我们必须设置一个定时器机制,虽然我支持你的长连接,但你不能占用我的连接资源那么长时间。既然你自己不关闭连接,我就设置一个定时功能,多长时间你还没跟我服务器通信我就直接关闭你这个连接,下次要通信你再重新发起连接吧。

这就是定时器模块的功能,它会记录每一个连接距离上一次发送数据给服务器的时间,然后设置一个超时时间,如果这个时间超过了超时时间,就自动销毁该连接,这就是muduo库的超时自动销毁连接机制。

线程池模块:
这个模块就是维护一个线程池,因为我们实现的muduo库是多Reactor多线程模型的,所以需要一个线程池来管理多个子线程,主Reactor接收到连接事件之后,就从线程池中选一个线程,让它去管理新连接后续的IO事件。

2.Reactor部分

Channel模块:
Channel模块是用来管理监控事件的,其实我们服务器要监控的事件就是这四类:可读事件、可写事件、错误事件、连接关闭事件,所以Channel模块要对这些监控的事件进行管理,比如你要设置监听的事件是可读事件还是可写事件,还要设置每一类事件触发以后的回调函数,这样事件触发以后才能够调用对应的回调函数去处理。

Poller模块:
Poller模块是对epoll函数操作的封装,但这个不是简单的封装,事实上Poller模块和Channel模块是关联起来的,Poller模块需要管理所有epoll模型要监控的文件描述符,这些文件描述符其实就是一个个的事件,所以每个文件描述符都对应一个Channel对象,Poller模块监控这些文件描述符,一旦有事件就绪,就调用这些文件描述符的Channel对象函数,去执行相应的回调函数,因为Channel模块是设置了每一种事件的回调函数的。

EventLoop模块:
EventLoop模块就是实现Reactor的模块了,它是对Channel模块和Poller模块的整体封装,在服务器中每创建一个EvevtLoop对象,其实就是创建一个Reactor,但是EventLoop也不是简单封装Reactor操作,事实上EventLoop模块是特别核心的一个模块,因为它关联了很多其他模块,比如Poller模块、Channel模块、Connection模块等,所以这个模块简述不清,后面会详细介绍。

Connection模块:
Connection模块是管理连接的模块,主Reactor监听到新连接到来后,就会创建一个Connection对象,用这个对象来管理这个新来的连接。这些管理包括读取数据、发送数据、启动非活跃连接超时销毁任务、释放连接、关闭连接等操作。另外,Connection模块还有一个很重要的成员就是EventLoop对象,因为主Reactor监听到新连接到来,创建Connection对象以后,需要把这个连接交给从属Reactor去监听事件,所以这个EventLoop对象就是从属Reactor。

3.TCPServer部分

TCPServer模块:
这个模块就是整个TCP服务器最上层的模块,它实现了对另外两个部分所有模块的封装,然后提供接口给外界,外界只需要调用相应的接口就能启动这个TCP服务器。

由于篇幅有限,我也不希望全部挤在一篇文章里来写,所以我分开几个文章来记录这个项目,这篇文章只是初步对muduo库做一个介绍,简单了解muduo库的背景知识以及基本的结构,接下来将针对每个模块详细地介绍项目的设计和实现过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/351713.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DB2数据库,时间类型插入数据

DB2数据库,时间类型插入数据 1、TIMESTAMP类型 1.1、创建表 CREATE TABLE BI_varchar ( id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1 ), hide_zero varchar(1000), simulation_cphone_de varchar(1000), disorder_de varchar(…

人工智能:更多有用的 Python 库

目录 前言 推荐 JupyterLab 入门 复杂的矩阵运算 其它人工智能和机器学习的 Python 库 前言 在这篇文章中,我们将了解更多的矩阵操作,同时再介绍几个人工智能 Python 库。 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂&#x…

【机组】基于FPGA的32位算术逻辑运算单元的设计(EP2C5扩充选配类)

​🌈个人主页:Sarapines Programmer🔥 系列专栏:《机组 | 模块单元实验》⏰诗赋清音:云生高巅梦远游, 星光点缀碧海愁。 山川深邃情难晤, 剑气凌云志自修。 目录 一、实验目的 二、实验要求 …

幻兽帕鲁 Mac 怎么玩? 手把手教你下载CrossOver 玩 Palworld 幻兽帕鲁游戏

《幻兽帕鲁》(英文:Palworld)是一款近期在 Steam 爆红的动作冒险生存游戏,游戏设置在一个居住着「帕鲁」的开放世界中,玩家可以战斗并捕捉帕鲁,也能用它们来建造基地、骑乘和战斗。 不过目前《幻兽帕鲁》仅…

3d gaussian splatting笔记(paper部分翻译)

本文为3DGS paper的部分翻译。 基于点的𝛼混合和 NeRF 风格的体积渲染本质上共享相同的图像形成模型。 具体来说,颜色 𝐶 由沿射线的体积渲染给出: 其中密度 𝜎、透射率 𝑇 和颜色 c 的样本是沿着射线以…

仅使用 Python 创建的 Web 应用程序(前端版本)第09章_购物车

在本章中,我们将实现购物车页面。 完成后的图像如下。 创建过程与之前相同,如下。 No分类内容1Model创建继承BaseDataModel的数据类Cart、CartItem2Service创建一个 CartAPIClient3Page定义PageId并创建继承自BasePage的页面类4Application将页面 ID 和页面类对添加到 Multi…

el-upload上传文件,如何使用action、auto-upload

1点击打开自动上传 没有 :auto-upload"false"就是自动上传 2点击上传按钮上传 不自动上传:auto-upload"false" <el-dialogstyle"background: rgb(18, 67, 112); border: 1px solid #409eff":title"上传文件":model-value"…

DS:单链表的实现(超详细!!)

创作不易&#xff0c;友友们点个三连吧&#xff01; 在博主的上一篇文章中&#xff0c;很详细地介绍了顺序表实现的过程以及如何去书写代码&#xff0c;如果没看过的友友们建议先去看看哦&#xff01; DS&#xff1a;顺序表的实现&#xff08;超详细&#xff01;&#xff01;&…

把批量M3U8网络视频地址转为MP4视频

在数字媒体时代&#xff0c;视频格式的转换已成为一项常见的需求。尤其对于那些经常处理网络视频的用户来说&#xff0c;将M3U8格式的视频转换为更常见的MP4格式是一项必备技能。幸运的是&#xff0c;现在有了固乔剪辑助手这款强大的工具&#xff0c;这一过程变得异常简单。下面…

linux 查看zookeeper server运行版本号

zookeeper版本查看运行命令&#xff1a;echo stat|nc localhost 2181 显示如下图所示&#xff1a; Zookeeper version: 3.4.5-cdh6.3.2--1, built on 11/08/2019 13:15 GMT Clients: /127.0.0.1:44814[0](queued0,recved1,sent0) Latency min/avg/max: 0/0/0 Received: 9 Se…

Linux——搭建FTP服务器

1、FTP简介 FTP(File Transfer Protocol) &#xff1a;是一种处于应用层的用于文件传输的协议。FTP客户端和FTP服务器之间的通信使用TCP/IP协议族。它规定了客户端和服务器之间的通信格式和命令集&#xff0c;包括用户认证、文件传输、文件名和目录信息等&#xff0c;允许用户…

专业133总分400+上海交通大学819考研经验分享上交819电子信息与通信工程

今年专业819信号系统与信号处理133&#xff0c;总分400&#xff0c;如愿考上梦中上海交通大学&#xff0c;通过自己将近一年的复习&#xff0c;实现了人生中目前为止最大的逆袭&#xff08;自己本科学校很普通&#xff09;&#xff0c;总结自己的复习经历&#xff0c;希望可以给…

时序预测 | MATLAB实现ICEEMDAN-SSA-GRU、ICEEMDAN-GRU、SSA-GRU、GRU时间序列预测对比

时序预测 | MATLAB实现ICEEMDAN-SSA-GRU、ICEEMDAN-GRU、SSA-GRU、GRU时间序列预测对比 目录 时序预测 | MATLAB实现ICEEMDAN-SSA-GRU、ICEEMDAN-GRU、SSA-GRU、GRU时间序列预测对比预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 时序预测 | MATLAB实现ICEEMDAN…

第七篇【传奇开心果】beeware的toga开发移动应用示例:gui工具包介绍和常用组件使用方法示例

传奇开心果博文系列 系列博文目录beeware的toga开发移动应用示例系列博文目录一、beeware和toga介绍二、Toga常用组件使用方法示例三、归纳总结系列博文目录 beeware的toga开发移动应用示例系列 博文目录 一、beeware和toga介绍 1.BeeWare介绍 BeeWare是一个可以让Python开…

C语言入门(二)、每日Linux(三)——gcc命令,通过gcc命令熟悉C语言程序实现的过程

使用gcc编译C语言程序 C语言程序实现的过程gcc命令基础用法常用选项编译和汇编选项&#xff1a;优化选项&#xff1a;调试选项&#xff1a;链接选项&#xff1a;警告选项&#xff1a; 实验对于-o选项 通过gcc命令熟悉C语言程序的执行过程1.预处理2.编译阶段3.汇编阶段4.链接阶段…

【Rose】用例图活动图绘制

绘制【Rose】用例图、活动图 文章目录 安装Rose后、画图前的准备工作绘制用例图为用例创建活动图描述用况 安装Rose后、画图前的准备工作 打开Rose&#xff0c;选择第一个J2EE就好。 渲染完的样子&#xff1a; 下面就可以开始画图了。✌✌ 绘制用例图 1、点击【Use Case V…

<蓝桥杯软件赛>零基础备赛20周--第18周--动态规划初步

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周。 在QQ群上交流答疑&am…

2024年【浙江省安全员-C证】考试题库及浙江省安全员-C证模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【浙江省安全员-C证】考试题库及浙江省安全员-C证模拟考试&#xff0c;包含浙江省安全员-C证考试题库答案和解析及浙江省安全员-C证模拟考试练习。安全生产模拟考试一点通结合国家浙江省安全员-C证考试最新大纲…

Vite+Electron快速构建一个VUE3桌面应用(一)

一. 简介 首先&#xff0c;介绍下vite和Electron。 Vite是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验。Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入Chromium和Node.js到二进制的 Electron 允许您保持一个 JavaScript 代码代码…

Java 魔法类 Unsafe 详解

&#x1f345;文末获取联系&#x1f345; &#x1f447;&#x1f3fb; 精彩项目推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;希望帮助更…