基于Java实现简单Reactor

Intro

Reactor最早是在这篇论文中提出的 作为Java课程拓展阅读的笔记,主要分析老师给的参考实现和论文中介绍的组件的对应关系。

一张解释了各个组件之间关系的图:

image-20211211134127551

InitiationDispatcher (以下简称ID)是事实上监听事件的组件,其中使用NIO的实现方式,因为实际上一个Reactor中ID尽可能的使用单线程的方式实现,如果轻易阻塞会极大影响效率。同时具有一张用于分发的EventHandler表,在事件注册的时候进行初始化。SynchronouseEventDemultiplexer是用于通知ID的组件。

EvenHandler和Handle实际上实现了“Dispatch”(分发)的功能,事件注册时需要在ID中注册。

在看懂Reactor的代码之前,需要理解网络通信,以及了解NIO的实现机制。

Reactor Basic

image-20211211135057739

可以理解为Reactor统一处理请求,再将请求根据具体的任务分发到具体的acceptor,交给对应的处理函数执行(demultiplexing)代码对应single文件夹,其中包括了基础的组件。

Handler(Runnable)对应了论文中的EventHandler,其中需要初始化socket,并且设置为NIO模式(将会使用到select)。

image-20211211135532346

此外,最重要的是注册事件。注册事件实际上除了绑定到特定的selectkey上,还会在启动run的时候绑定具体的处理函数。

image-20211211135554150

特别的是,所有事件注册在同一个Reactor的socket上,因此Accept事件结束后要用interestOps注册下一个需要被处理的事件。

Reactor类中的run中的循环实际上对应着论文中的ID,进行监听,select实现了NIO,在监听到事件后进行分发。其中的dispatch函数就是提取之前注册在selectkey上的handler对应的具体的Runnable来执行。

Basic的缺点是,在执行任务的一侧,依旧是多线程,并没有解决最开始希望减少线程开销的目标。因此在Pool版中使用了线程池来有效管理资源。

Pool Reactor

image-20211211140352313

除了增加线程池相关,其他代码与basic没有太大差别。

Multiple Reactor

注意到上述两种,不同事件都注册在同一个selector上,在同一个线程中处理,而不同的事件处理时长可能不同,在同一个线程中可能会阻塞。因此考虑把事件分类,并用不同的selector处理。提高效率的同时增加了拓展性。

image-20211211140628740

示例代码中把事件分为连接事件和操作事件,MainReactor和SubReactor分别进行处理。在这种模式下,thread的使用模式(单线程/无限制的多线程/线程池)不受限制。