Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?

前言

最近在学习Netty相关知识,碰到NIO和一大堆概念实在搞不清楚,见不得网上千篇一律的转载和抄袭,为了搞清楚概念必须针对性的学习下了。看这篇文章前十分建议先浏览学习下消息订阅Java实现
Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?

多种模型

首先,我们了解下select/poll/kqueue/epoll四种模型的特点,这些都是系统来进行实现的方法。
这里关注下Select

  • Select 主要是用来阻塞的接收客户端传来的请求,能同时监听1024个。

NIO模型

NIO也叫做同步非阻塞IO,有些地方也叫做New IO。
对于传统的IO,每当获取到一个连接就会有一个线程与之所对应,就像一个顾客对应一位服务员,不管客户有没有需求都在餐桌旁边候着。而NIO就相当于这个服务员在你没有需求只顾吃饭的时候会去为别的顾客服务。事件驱动的事件就是相当于点菜的需求。

IO多路复用

在查阅了资料之后,我才知道IO多路复用就是这样的一种形式:
我们知道select可以用来阻塞的获取socket的信息,所以我们可以使用单线程,传入多个socket进行监听,每当一个sokect有事件就通知事件处理器进行处理。

Reactor模式

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.

据*介绍,Reactor模式是一种事件处理的模式,将多个输入同时交付给服务处理程序,然后对请求进行多路分解,并同步地分发到到对应的处理器中。

网上也有很多的介绍,例如下图
Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?

这里介绍了Reactor的各个处理模块,但是光看这图,本人确实有些看不懂,下图是我对图的重新理解。可以结合起来学习。
Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?

  • 首先初始化InitiationDispatcher,将处理事件的Handler初始化到EventHandler的Map中,然后调用register_handler方法将EventHandler注册到InitiationDispatcher
  • 然后InitiationDispatcher调用handler_events()启动EventLoop进行阻塞等待,这里主要通过Synchronous Event Demultiplexer的Select()方法进行阻塞等待Event发生。
  • 在出现了event,像读取数据,写入数据的event时会接着调用handler_events方法进行轮训调用注册在上面的handler的handler_event(type).
  • 期间当获取到注册请求可以进行新的handler的注册

这个模式和我之前讨论过的消息订阅有几分类似,看不太懂的可以先去看看消息订阅的简单实现。

Reactor mode三种实现

Reactor 单线程

Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?
每个客户端发起连接请求都会交给acceptor,acceptor根据事件类型交给线程handler处理,但是由于在同一线程中,容易产生一个handler阻塞影响其他的情况。

Reactor 多线程

Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?
这里使用了单线程进行接收客户端的连接,采用了NIO的线程池用来处理客户端对应的IO操作,由于客户端连接较多,有时会一个线程对应处理多个连接。

Reactor 主从

Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?
这里将接收客户端请求后采用线程池进行处理,服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到IO线程池(sub reactor线程池)的某个IO线程上,由它负责SocketChannel的读写和编解码工作。Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操作。

Redis中使用

Redis中就是使用到了IO多路复用的机制,由于其内部是单线程处理的,所以对应的是单线程的Reactor模式。因为Redis中的命令通常很简短,所以不容易产生阻塞的情况,并且由于单线程减少了线程间上下文切换的消耗,大大提高了效率。
Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?

Netty中使用

Netty中是实现是建立在JDK中NIO的实现之上的。对于传统JDK nio优化了ByteBuffer,内部实现了ByteBuf。
它的线程模型和Reactor的主从模型比较接近。
具体参考本文netty线程模型
netty的nio线程默认线程是系统核心数目的两倍
Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?

Reactor模式到底是什么?和NIO有什么关系?为什么Redis,Netty都用到了?

NettyRuntime是系统类Runtime的封装,这里默认获取了当前系统核心数的两倍线程。

小结

IO多路复用使用了NIO模型
Reactor模式使用了IO多路复用的机制
Redis使用了Reactor模式
Netty优化了Java的NIO并且使用了类似Reactor的模式

可能目前理解的还不是很正确,如果有大佬觉的哪里不合适可以评论下指出。