前言
- 前文精彩回顾,基于epoll封装一个简单的reactor反应堆模式, 事件循环
一. 为什么要使用Reactor设计模式
- 并发编程的最初模型 --- 多线程阻塞IO模型
缺点1: 每一个connect 都对应一个线程,线程占用系统资源,并发数过大时,系统资源不足以支撑. (系统资源上限制并发量)
缺点2: 阻塞IO,在等待IO到来的时候会阻塞住线程,阻塞占用线程, 线程利用率低下,且线程数量有限,是对资源的一种极大的浪费 (占着线程不工作)
解决阻塞IO + 多线程资源浪费 --- Reactor模型
基于池化思想,避免为每个连接创建线程,连接完成后将业务处理交给线程池处理. (开启work_threads线程池处理业务需求) --- 实现网络IO 跟 业务处理的解耦合 同时避免为每一个连接创建线程
基于IO复用技术,多个连接阻塞在同一个对象上,多个连接中出现IO事件的触发,操作系统就会通知应用程序处理 --- 还是阻塞, 但是是多个IO阻塞在一个对象上, 所以多路:就是多个IO,复用 :复用一个线程
理解IO复用:对比一下普通阻塞IO: 一个IO事件单独阻塞一个线程, 多路IO复用: 多个IO事件共同阻塞一个线程 --- 将多个IO阻塞等待 重合阻塞在一起。提高线程利用率.
Reactor : 集合IO复用 + 线程池 思想. IO驱动---> 事件驱动 (事件循环)
二.Reactor线程模型分类
根据Reactor的数量和处理资源的线程数量的不同,分为三类:
在一个Reactor中完成所有事情, IO事件注册,IO事件分发 IO事件处理 --- 单个线程中实现
缺陷:
单个线程处理所有请求, 对于多核CPU着实是一种浪费
当处理读写任务的线程负载过高后,处理速度下降,事件会堆积,严重的会超时,可能导致客户端重新发送请求,性能越来越差
不能支持高接入量, 服务器的接入量低, 因为在 accept 建立连接的请求中夹杂了大量的业务处理,业务处理耗时长, 造成accept占比低下,能同时支持的用户接入量低下
典型代表实例:redis 内存数据库 操作redis当中的数据结构
- 单Reactor多线程模型
和单Reactor单线程相比核心在于将业务逻辑从Reactor中剥离出来放入work_threads中进行处理,实现网络IO 和 业务处理之间的解耦合。 充分利用多核资源,提高性能, 提高了用户接入量和可靠性
缺陷:
Reactor线程承担所有的事件,既要处理新连接的建立,又要处理read, send IO事件, 高并发场景下单线程存在性能问题,并且对于高接入量场景下无法及时响应大量的acceptor.
代表:skynet
- 多Reactor多线程模型
这种模型下和第二种模型相比是把Reactor线程拆分了mainReactor和subReactor两个部分,mainReactor只处理连接事件,读写事件交给subReactor来处理。业务逻辑还是由线程池来处理.
mainRactor只处理连接事件,用一个线程来处理就好。处理读写事件的subReactor个数一般和CPU数量相等,一个subReactor对应一个线程,业务逻辑由线程池处理 (单独的main线程 mainReactor 专门处理连接, 可以大大的提高接入量 ) 可靠性大大提高
这种模型使各个模块职责单一,降低耦合度,性能和稳定性都有提高
这种模型在许多项目中广泛应用,比如Netty的主从线程模型 memcached 等
三.Reactor线程模型生活化
餐厅一般有接待员和服务员,接待员负责在门口接待顾客,服务员负责全程服务顾客
Reactor的三种线程模型可以用接待员和服务员类比
单Reactor单线程模型:接待员和服务员是同一个人,一直为顾客服务。客流量较少适合
单Reactor多线程模型:一个接待员,多个服务员。客流量大,一个人忙不过来,由专门的接待员在门口接待顾客,然后安排好桌子后,由一个服务员一直服务,一般每个服务员负责一片中的几张桌子
多Reactor多线程模型:多个接待员,多个服务员。这种就是客流量太大了,一个接待员忙不过来了
四. 总结本章
本章从为传统的并发服务器:多线程阻塞IO 的弊端入手引出来为什么需要Reactor模式
多线程阻塞IO : 主要弊端是:可以创建的线程量有限 --- 并发量限制
阻塞IO : 阻塞线程,线程的利用率有限 --- 线程利用率不高
线程池: 解决线程创建数量有限的问题, 复用线程 (网络IO 业务逻辑解除耦合性,避免一个连接创建一个线程)
IO复用技术:解决大量线程阻塞线程利用率不高的弊端.
单个Reactor 单个线程处理网络IO连接 读写操作 + 业务逻辑. 网络模型不稳定,超高并发情况下不可靠,不能及时处理客户的连接请求等弊端,接入量低
单Reactor + 多线程(线程池) 使用work_threads 来处理业务逻辑, 将网络IO和业务逻辑解除耦合性, 提高服务器面对高并发时候的稳定性, 可靠性, 但是对于突然的超多接入量情况下还是存在缺陷,因为Reactor 既处理acceptor 还处理 IO read write。而且也没有充分利用起来多核CPU的优势
多Reactor + 线程池 模型,mainReactor仅仅只是处理 acceptor 连接, subReactor来处理 IO read write 操作. 大大提高了接入量 + 大大提高服务器的稳定新 和 可靠性,各个模块之间低耦合 且职责单一,更符合设计模式的要求