1. Reactor设计模式
Reactor 模式的基本设计思想是基于I/O复用模型来实现的。
这里说下I/O复用模型。和传统IO多线程阻塞不同,I/O复用模型中多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象等待。当某个连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。
什么意思呢?餐厅老板也发现了顾客点餐慢的问题,于是他采用了一种大胆的方式,只留了一个服务员。当客人点餐的时候,这个服务员就去招待别的客人,客人点好餐后直接喊服务员来进行服务。这里的顾客和服务员可以分别看作多个连接和一个线程。服务员阻塞在一个顾客那里,当有别的顾客点好餐后,她就立刻去服务其他的顾客。
了解了 reactor 的设计思想后,再来看下单 reactor 单线程的实现方案:
Reactor通过 I/O复用程序监控客户端请求事件,收到事件后通过任务分派器进行分发。针对建立连接请求事件,通过 Acceptor 处理,并建立对应的 handler 负责后续业务处理。针对非连接事件,Reactor 会调用对应的 handler 完成 read->业务处理->write 处理流程,并将结果返回给客户端。整个过程都在一个线程里完成。
2. Redis的网络事件处理器
Redis基于Reactor模式开发了自己的网络事件处理器,这个处理器被称为文件事件处理器。I/O多路复用程序负责监听多个套接字,并向文件事件分派器传送那些产生了事件的套接字。尽管多个文件事件可能会并发地出现,但I/O多路复用程序总是会将所有产生事件的套接字都放到一个队列里面,然后通过这个队列,以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字。当上一个套接字产生的事件被处理完毕之后(该套接字为事件所关联的事件处理器执行完毕),I/O多路复用程序才会继续向文件事件分派器传送下一个套接字。整个过程都在一个线程里完成,因此 Redis 被称为是单线程的操作。
3. Redis6.0的多线程
Redis6 版本中引入了多线程。之前已经提到过 Redis 单线程处理有着很快的速度,那为什么还要引入多线程呢?单线程的瓶颈在什么地方?
先来看第二个问题,在 Redis 中,单线程的性能瓶颈主要在网络IO操作上。也就是在读写网络的read/write系统调用执行期间会占用大部分CPU时间。如果要对一些大的键值对进行删除操作的话,在短时间内是删不完的,那么对于单线程来说就会阻塞后边的操作。回想下上边讲得 Reactor 模式中单线程的处理方式。针对非连接事件,Reactor 会调用对应的 handler 完成 read->业务处理->write 处理流程,也就是说这一步会造成性能上的瓶颈。
Redis6.0的多线程是指,将网络数据读写和协议解析通过多线程的方式来处理,对于命令执行来说,仍然使用单线程操作。也就是说,Redis6.0的多线程是为了解决其网络IO的瓶颈。
遗留问题
红色字体部分待理解
参考文献