muduo源码总结

1.对Reactor模式的理解

个人理解Reactor(反应堆)模型,就是用一个线程来监听不止一个文件描述符,每当一个文件描述符产生被关注的事件时,就会分发给其他线程或者自己来解决这个事件。其核心我觉得就是使用epoll实现多路复用,也就是一个线程可以实现多个文件描述符的监听。

2.muduo的模式总结

muduo的编程模型称之为one loop per thread,我觉得也可以称之为Multiple Reactors,其解释就是每一个IO线程都有一个Eventloop类,其实每一个Eventloop类就可以称为一个Reactor,可以监听多个文件描述符。以一个比较复杂和完整的muduo运行框架图来展示一下:

这是一个由muduo组成的最复杂的模型结构,首先有两个Reactor,一个主Reactor,一个子Reactor,

主Reactor对应muduo源码就是TCPServer,是负责监听socket套接字的,Acceptor类就是在这个IO线程中执行的,也就是负责与客户端建立连接 

子Reactor则是负责与连接套接字通信的,每个子Reactor绑定一个TCPconnection类,每次与一个客户端建立连接以后,可以获得一个连接套接字,这个套接字就会挂在子Reactor的epoll队列中,然后由TCPconnection类负责和这个连接套接字进行read和send。并且可以设置不止一个子Reactor,每个Reactor上都绑定一个TCPconnection类,因为IO操作比较费时间,所以当多个客户端连接时,可以使用多个子Reactor来负责,这就是EventLoopThreadPool,子线程的线程池。

然后还有一个ThreadPool,主要是负责计算任务的,当子Reactor读取客户端发来的数据以后,如果计算量特别大,并且也不涉及IO操作,那么用子Reactor来计算很浪费资源,所以就开设一个线程池,来专门进行计算,并将计算的结果发给子Reactor,然后再发送出去。

注意:整个结构,并不是所有部分都需要的,其实可以根据具体的情况,有所取舍。比如并发性不高,IO要求不高,可以只用主Reactor既负责监听套接字,也负责连接套接字的处理,可以把子Reactor舍去。比如计算量不大,可以不用线程池,计算任务在Reactor线程中进行。最简单的模式就是只有一个主Reactor,具体代码可以参考echo程序。

在最复杂的模式下,threadPool类绑定在主Reactor的线程下,也就是主Reactor负责开启线程池和管理线程池,所有的子Reactor读到的数据再从线程池中拿一条线程来计算。

3.muduo中源码的头文件总结

base

LogFile.h

主要是与日志文件的IO函数,其中有两个类,一个File类,主要是用来建立用户对单个文件的进程缓冲区,以及打开,追加等文件IO函数。另一个LogFile类封装了File类,主要实现滚动日志文件的功能(也就是当一个日志文件过大,或者写入时间过长时,就关闭这个日志文件,重新打开一个新文件)。

AsyncLogging.h

是实现异步日志的头文件,用户需要实现一步日志主要依靠这个头文件。主要就两个函数threadFunc和append,append函数使用是前端工作线程,如果产生了日志,就会把日志存到双缓存之中。threadFunc则是由专门负责写入日志的线程操作LogFile类来写入到对应的IO日志文件中去。

注意:前端线程写入的缓存区是使用的LogStream.h中的FixedBuffer缓存实现的,而后端写入到文件之间也有一个缓存,是靠简单的char数组,依靠setbuffer函数实现的。

LogStream.h

其中主要有FixedBuffer类,是一个缓冲区的封装,LogStream,是缓冲区流的封装,也就是封装了缓冲区,并且重载了一系列的<<符号。以及类Fmt,是用来处理小数的

Logging.h

其中有SourceFile类,是用来根据路径获得文件名,主要是用来由linux中的__FILE__宏获得执行程序所在的文件名,Impl类则是封装了LogStream类,可以将对应内容用符号<<输出到缓冲区中。最后就是Logger类,其实就是组织日志的内容,先存入到缓存中,然后再从缓存中输出到对应的设备或者文件中。其中默认的设备是标准输出。

Atomic.h

实现一个原子操作的计数器类

BlockingQueue.h

是一个线程安全的无界阻塞队列,就是队列的长度没有限制,并且在队列为空时,会进行条件阻塞

BoundedBlockingQueue.h

是一个线程安全的有界阻塞队列,和BlockingQueue.h类似

Condition.h

封装了条件变量的一系列函数

copyable.h

可复制类,就是一个空类

CountDownLatch.h

是一个封装了互斥锁和条件变量以及一个计数器的类,当计数器为0时可以唤醒所有线程,当计数器不为0时,会条件阻塞等待。

CurrentThread.h

获取了当前进程和线程的一些信息

Exception.h

出现异常的显示类

FileUtil.h

对文件内容的读写

Mutex.h

对互斥锁的封装

ProcessInfo.h

从linux的/proc目录下获取进程相关信息

Singleton.h

单例模式,其中成员变量中只有一个静态指针是有用的

StringPiece.h

封装了字符串,用以实现高效的字符串传递,原理就是这个类的私有变量就是char指针和一个字符串长度,所以在传递这个类的字符串时,只需要传递字符串的指针和长度即可,不需要整个字符串都拷贝过去

Thread.h

对创建子线程的函数封装,以及获得一些线程信息的函数

ThreadLocal.h

线程本地类,保证存在这个类中的私有变量,是每个线程单独拥有的,而不会互相干扰。

ThreadLocalSingleton.h

既是线程本地类,又是单例模式。也就是这个类只能有一个类变量,并且这个类变量可以在每个线程中单独修改,而不互相影响

ThreadPool.h

最普通的线程池,我觉得其中在signal前应该加一个判断更加稳妥一些,就是判断是否有空闲线程在池中等待

Timestamp.h

时间戳类,私有变量是一个从1970年1月1日到当前的总微秒数,可以通过静态成员变量now来获得当前时间

types.h

就是一些强制类型转换的封装

net

Acceptor.h

在Acceptor类中会创建一个Channel来处理监听套接字的可读事件,并且也封装了套接字的创建,绑定,监听等函数

Buffer.h

是一个缓冲区类,主要用于与客户端的连接套接字通信时,先从连接套接字将数据读到缓冲区中,或者将缓冲区中数据写入套接字,在TCpConnection中使用

Callbacks.h

主要是对函数指针的定义

Channel.h

Channel类是最小的事件处理单元,每个Channel会绑定一个套接字和一个Eventloop,如果这个套接字发生的事件被监听到,就会调用这个Channel中的回调函数去处理事件,另外监听这个套接字,以及处理这个套接字的线程就是绑定的Eventloop的IO线程。

Connector.h

Connector封装了客户端的Connect函数,在连接成功后会监听连接描述符,并调用回调函数处理事件

Endian.h

主要封装了linux特有的字节序转换函数

EventLoop.h

一个Eventloop变量就代表一个Reactor,是基于Reactor的核心类,主要作用就是维护一个epoll队列,并阻塞监听事件的发生,还封装了几个定时器的函数。

EventLoopThread.h

封装了一个可以创建IO线程的线程类,主要用于Eventloop线程池

EventLoopThreadPool.h

封装了IO线程的线程池

InetAddress.h

这个类主要用来初始化sockaddr_in类结构体,只需要输入端口号和IP,就可以自动生成sockaddr_in结构体

Poller.h

是EPollPoller的父类, 其中函数都是纯虚函数,需要用子类中的函数来实现

EPollPoller.h

封装了epoll的函数,并且里面的文件描述符用channel类来包装了

DefaultPoller.cc

定义了创建poller类的函数

Socket.h

用socket类封装了一系列操作socket的函数,包括bind,listen,accept等,在Acceptor类中使用到

SocketsOps.h

封装了一系列操作socket的全局函数,在命名空间sockets中

TcpClient.h

用户可以直接继承或者定义这个类来生成TCP客户端,里面主要包含Connector类用来连接,TcpConnection用来从套接字中收发数据。

TcpConnection.h

主要用来监听连接套接字,与套接字通信,是TCP的核心类,其中用Buffer缓存来实现进程内的缓存。每个连接上的客户端都有一个单独的TcpConnection与之对应。

TcpServer.h

用户可以直接继承或者定义这个类来生成服务端程序,其中主要包含了Acceptor类用来监听端口,和每一个客户端建立一次连接,就会创建一个TcpConnection变量来负责和这个客户端通信。并且这些TcpConnection是依靠shared_ptr指针来管理和自动销毁的。

Timer.h

是一个定时器类,但是并没有真正计时的功能,只是用来设定超时时间。

TimerId.h

是一个用RAII思想封装的Timer类,就是在构造函数中赋值,在析构函数中销毁值。

TimerQueue.h

定时器管理类,其中timer类就是TimerQueue需要管理的元素,而timerId就是一个简单的timer封装,避免销毁和创建操作但是要注意的是timer并没有自己计时的功能,所以需要依靠timerfd这个系统函数统一计时timerfd是一个系统计时函数,当所设置的时间到了,会通过timerfd这个文件描述符进行提示通信,而其他计时函数可能是通过信号量,或者其他方式,但是都没有文件描述符好,并且也可以用timerfd监听,具体原因可以查看一下博客的网络库定时器实现如何使用timerfd来为所有的计时器计时:timerfd每次都设置在计时器列表中到期时间最近的那个到期时间,这样timerfd到期以后,也就是最近的那个计时器到期所以每次都是手动重置timerfd的计时时间,为最近的计时器到期时间

4.C++源码阅读经验

在看一个C++类时,最主要的是看成员变量,而不是看成员函数,成员函数都是围绕成员变量实现的,所以先搞清楚成员变量代表的含义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值