- muduo源码学习 Day01
- 前言
- 一、reactor是什么?
- 二、不同的小r,不同的打工人
- 1.单Reactor 单线程/进程
- 2.单Reactor 多线程/进程
- 3.多Reactor 多线程/进程
- 三、总结
前言
十分感谢小林coding的图解系统,和muduo网络库,linux高性能服务器这两本书,让我能坚持学习下去…
图片和部分内容整理自小林coding网站
从认识reactor出发,究竟什么是reactor,单线程的reactor的是怎么实现的?什么是多线程,多线程并发体现在哪? 我想在接下来的一段时间,这些问题一直伴随我,索性就把这段时间记录下来
Reactor模式 简单来说就是事件驱动的分发机制 reactor可以看作是Reactor模式的成员。如果要学习他,不妨考虑从了解它是用来干什么,用来解决什么样的问题的角度出发。
一个服务器如果要同时服务很多客户端,简单的办法就是为每条连接单独分配一个线程,结束这个连接再销毁这个线程,再来新的再创建,等他go die再销毁,循环反复,线程的频繁创建销毁会带来“巨大“的开销,效率不高。如何解决这个问题---------------资源复用,也就是池。
通过池的思想我们不必再去不停地创建销毁,创建销毁,但是也引入了新的问题,线程池里工作线程的数量是有限的,以前一个线程只有一个连接,read阻塞也就阻塞了,其他连接对应的线程正常工作,大家相安无事,现在一个线程势必面对好多个连接,一个连接的read阻塞了,同一个线程的其他连接怎么办?如何解决这个问题---------------sockfd设置成非阻塞,轮询,不断地问,你有数据吗?你有数据吗?有的连接都没有IO事件发生,你还问他,效率不高。那能不能有个好心人帮我盯着这些连接,等他们有数据了,来告诉我,我再去处理。-----------------------有,好心人是内核,方法是IO复用。
IO复用会通过系统调用替我们监听这些连接(当然它能监听的不仅仅是sockfd)等他们有数据了,告诉我们去读,等读好了,告诉我们去写,贴心!通过这种方式,我们知道了那个连接需要去处理,也知道了这个连接需要处理的事件是什么?我们还有了打工人,那怎么给这些打工人分配这些事件,这就是Reactor做的事,也就是事件驱动。
通过上面的分析,我们不难看出Reactor模式中有人负责“接活“和“派活”,有人负责“干活”,前者就是reactor,后者在上述模型中就是池。reactor姑且称为小r,他可能是一个人,也可能是一个部门(有上下级的那种,也就是主从reactor),打工人多种多样,可以是全能型人才(后面会介绍她是怎么做到的,工作线程可能它的工作内容不一样他就根据不同的事件类型去执行不同的事件,力工,水工,电焊工),也可以是从始至终只为一个人服务的专一暖男。
Reactor模式组成灵活多变, 根据不同的小r和不同的打工人,Reactor可以分为 单Reactor 单线程/进程 ,单Reactor 多线程/进程,多Reactor 多线程/进程,多Reactor单进程/单线程被自然界淘汰掉了。
如果说把小r看做领导,那么打工人就有三种人,一种是Accepter(业务经理),一种是Handler(业务员),还有一种就是苦命人(zz)。
1.单Reactor 单线程/进程客户来了(新的连接来了),小r(派遣)调用Accepter 去接待,创建了这个连接(这个连接不仅仅是指的是accept返回的新文件描述符cfd,往往被封装成一个对象,对象里面有好多成员,后面会继续学习,一定要记住,这个连接往往是个对象,对象,对象),我们为这个对象分配了(创建)一个业务员handler,handler就跟这个客户绑定了(成员初始化),这个对象的handler负责给这个对象干活(绕一点就绕一点吧),注:这种模式下没有池(肯定没有,单线程啊),每个连接的所有大活小活都让handler干了。
优点:都在一个线程里,没有进程间通信,不用考虑线程安全。
缺点:在一个进程里,同一时间,我们只能看到一个对象他的handler在干活,不能很好的利用CPU多核的优势,还有如果是计算密集型的场景,一个handler碰见了一个史诗级的数学问题,就给他算死了,他算的时候别的连接的handler只能等着,因为单线程,工作室只有一间,可能会造成响应的延时。
它只适用于单个业务逻辑处理很快的场景,比如redis,redis为什么快?-----------------------------内存,内存,内存,基于内存(多路复用和编码规范只是次要原因)
2.单Reactor 多线程/进程
记住一句话----------------------------------------------------------------------主线程只负责数据的接收和发送,handler不再当怨种。
客户来了(新的连接来了),小r(派遣)调用Accepter 去接待,创建了这个连接`为这个对象分配了专属的业务员handler,handler找了线程池里的工作线程去干(监工),我给你任务(回调函数),你负责把这个活给我干了,然后把你干完的数据再发给我,我拿给客户,然后工作线程不停地从任务队列里取任务,24h的加班干。。。。。。
宏观上看,同一时间有很多个对象的handler找的苦力在干活,泪目,这就是并发(但是对于每一个苦命人来说,他也不知道啥是并发,对他来说就是串行)。
优点:显然,解决了单线程的一些缺点,发挥了多核的性能(还要打上引号,毕竟加锁解锁的等等等等都要算进去)。
缺点:handler和线程池中的子线程势必要进行通信,安全,安全,安全,整不好就死锁。性能瓶颈,瞬时高并发,小r承受不了
mainreactor ------------------------- 领导
accepter -----------------------金牌业务经理
subreactor ----------------------小r
handler ----------------------业务员
mainreactor门面担当 通过主reactor招揽顾客,创建连接,让Accepter(金牌业务经理)给每位客户分配业务员handler,把创建好的连接对象交给loop池。
上文的打工池,变成了loop(循环)池,一个loop独享一个线程,一个连接对象它的所有操作都要在一个loop中完成(别的loop调用怎么办,自有妙计,后面会学习到),one loop one thread(每一次读这句话都感觉大自然的神奇…)
handler和单线程中的handler近似,一个人独揽全“活“,宏观上看,同一时间多个loop中(每个loop中)的连接的一个handler在干活,也就是很多个handler在干活,实现并发,较好的利用了多核CPU的性能。
总而言之,Reactor就是一种事件分发的模型,把要处理的时间派发下去,实现“并发“,解决各种场景中出现的性能问题,以上就是我粗浅的理解,如有错误,希望能得到批评指正…
继续学习------------------------------------------------------------------------------------------------------------------------------------------------------------------
2022-05-16



