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

news2024/9/30 11:23:01

文章目录

  • 一、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库的背景知识以及基本的结构,接下来将针对每个模块详细地介绍项目的设计和实现过程。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode:二叉树的中序遍历(外加先序,后序遍历)

题外:另外三种遍历可以看这: 层序遍历: Leetcode:二分搜索树层次遍历-CSDN博客 先序遍历: 二叉树的先序,中序,后序遍历-CSDN博客 后序遍历: 二叉树的先序,中序,后序…

办公技巧:PPT制作技巧分享,值得收藏

目录 1、黑屏/白屏你用过么 2、图形组合替代动画刷 3、等距分布图形元素 4、快速统一字体 5、文本框也是可以改的 6、批量修改形状 7、搞定“怎么也选不中” 8、妙用CtrlD 9、图片阵列怎么做 10、临时放大某一区域 11、Word快速导入PPT 12、炫酷小人怎么做的&#…

HAL STM32基于系统滴答定时器(SysTick)实现多任务时间片轮询

HAL STM32基于系统滴答定时器(SysTick)实现多任务时间片轮询 📑RTOS(实时操作系统)和定时器时间片轮询是两种不同的任务调度和执行方式的差异简介 🔖 以下部分内容,由AI给出的解答: …

中科星图——MOD11A1 V6是由Aqua中分辨率成像光谱仪获取的地表温度/发射率(LST/E)数据集

数据名称: MYD11A1.006 数据来源: NASA 时空范围: 2000-2022年 空间范围: 全国 数据简介: MOD11A1 V6数据集是由Aqua星搭载的中分辨率成像光谱仪获取的地表温度/发射率(LST/E)产品,以分片方式组织…

SpringBoot activemq收发消息、配置及原理

SpringBoot集成消息处理框架 Spring framework提供了对JMS和AMQP消息框架的无缝集成,为Spring项目使用消息处理框架提供了极大的便利。 与Spring framework相比,Spring Boot更近了一步,通过auto-configuration机制实现了对jms及amqp主流框架…

《动手学深度学习(PyTorch版)》笔记4.5

注:书中对代码的讲解并不详细,本文对很多细节做了详细注释。另外,书上的源代码是在Jupyter Notebook上运行的,较为分散,本文将代码集中起来,并加以完善,全部用vscode在python 3.9.18下测试通过。…

spring-boot-starter-validation常用注解

文章目录 一、使用二、常用注解三、Valid or Validated ?四、分组校验1. 分组校验的基本概念2. 定义验证组3. 应用分组到模型4. 在控制器中使用分组5. 总结 一、使用 要使用这些注解,首先确保在你的 Spring Boot 应用的 pom.xml 文件中添加了 spring-bo…

Facebook 广告帐户:多账号运营如何防止封号?

Facebook目前是全球最受欢迎的社交媒体平台之一,拥有超过27亿活跃用户。因此,它已成为个人和企业向全球受众宣传其产品和服务的重要平台。 然而,Facebook 制定了广告商必须遵守的严格政策和准则,以确保其广告的质量和相关性&…

项目风采展示【TRDa】

桌面功能介绍: 1:支持本地音乐、三方音乐控制播放展示功能; 2:支持陀螺仪 3:支持蓝牙列表显示。

【QT+QGIS跨平台编译】之十二:【libpng+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文件目录 一、libpng介绍二、文件下载三、文件分析四、pro文件五、编译实践一、libpng介绍 PNG(Portable Network Graphics,便携式网络图形),是一种采用无损压缩算法的位图格式,支持索引、灰度、RGB三种颜色方案以及Alpha通道等特性。 PNG使用从LZ77派生的无损数据压缩算…

RectMask2D的合批情况验证

1.经过实际测试,RectMask2D在裁剪区域完全相同且位置完全重合的情况下能够合批 但是当RectMask2D位置不重合时,就不能合批 注意,虽然此处被RectMask2D裁剪了,但是有部分是被渲染的,在计算深度时属于需要渲染…

20240126请问在ubuntu20.04.6下让GTX1080显卡让whisper工作在large模式下?

20240126请问在ubuntu20.04.6下让GTX1080显卡让whisper工作在large模式下? 2024/1/26 21:19 问GTX1080模式使用large该如何配置呢? 这个问题没有完成,可能需要使用使用显存更大的显卡了! 比如GTX1080Ti 11GB,更猛的可…

hex 尽然可以 设置透明度,透明度参数对比图 已解决

还不知道CSS Color Module Level 4标准早在2014年就推出8位hex和4位hex来支持设置alpha值,以实现hex和rgba的互转。这个办法可比6位HEX转RGBA简洁多了,先来简单解释一下: 8位hex是在6位hex基础上加后两位来表示alpha值,00表示完全…

计算机网络——TCP协议

💡TCP的可靠不在于它是否可以把数据100%传输过去,而是 1.发送方发去数据后,可以知道接收方是否收到数据;2.如果接收方没收到,可以有补救手段; 图1.TCP组成图 TCP的可靠性是付出代价的,即传输效率…

gitee仓库使用中的警告

当 Git 执行 git pull 命令时,有时候会出现类似下面的警告信息: warning: ----------------- SECURITY WARNING ---------------- warning: | TLS certificate verification has been disabled! | warning: ------------------------------------------…

光耦固态继电器在军工行业的全面应用分析

光耦固态继电器是一种集成了光耦隔离和固态继电器功能于一体的电子元件。它通过光电耦合技术实现输入和输出之间的电气隔离,同时利用固态开关控制输出电流。在军工行业,这一技术的应用正在逐渐得到广泛认可。 光耦固态继电器在军工设备中的电气隔离应用 …

FFMPEG解析ts流

三篇相关联的文章: ffmpeg下HLS解析过程-CSDN博客TS文件格式详解及解封装过程-CSDN博客 FFMPEG解析ts流-CSDN博客 一、简介 关于TS格式解析,可以参考《TS文件格式详解及解封装过程-CSDN博客》,本文主要代码部分解读。建议大家熟读iso138…

企业职能部门员工忙闲不均,如何调动积极性?

案例企业背景: 某企业隶属于中国航天科技集团公司,致力于光纤陀螺系统、微机电惯性系统、光纤传感系统等高新技术产品的研发。公司具有雄厚的新型惯导和光电传感技术基础,多年来开创了我国光纤陀螺技术在武器、卫星和载人飞船等多个任务上的…

深入浅出 diffusion(4):pytorch 实现简单 diffusion

1. 训练和采样流程 2. 无条件实现 import torch, time, os import numpy as np import torch.nn as nn import torch.optim as optim from torchvision.datasets import MNIST from torchvision import transforms from torch.utils.data import DataLoader from torchvision.…

Flink 集成 Debezium Confluent Avro ( format=debezium-avro-confluent )

博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,京东购书链接:https://item.jd.com/12677623.html,扫描左侧二维…