Reactor模式的详细介绍以及netty对其的具体实现

 关于Reactor与Proactor模式在上一篇文章中有过简单的介绍,本文想主要介绍一下Reactor模式,因为无论是java的Nio还是netty都很好的使用了Reactor模式,可见这种模式的重要作用,本文主要参考了并发大神Doug Lea在《Scalable IO in Java》一文中的一些讲解,同时也参考了 Jim Coplien 和Douglas C. Schmidt在95年发表在的一篇论文《Reactor》,本文的一些图片也截取自两篇文章。
 首先要理解Reactor模式是一种网络编程模式,在了解Reactor之前有必要先看下传统的网络编程模型–堵塞IO模型:
Reactor模式的详细介绍以及netty对其的具体实现
 在图中可以清楚的看到,当客户端连接服务端的时候每一个客户端连接过来服务端都要开启一个线程对其进行处理,很明显在这种模式下如果客户端的连接过多服务端就会产生大量的线程,这无疑会增大CPU的开销以及线程上下文的切换,这是十分耗费系统资源的,这种模式在单机连接数小于1000的情况下还是很实用的但是随着连接数增多这种模式很明显满足不了业务需求。
 看到了传统的IO模型的不足我们来介绍下Reactor模式,Reactor模式在Doug Lea的描述中是分为三种实现的即Basic version(基本版本) 、Multithreaded versions(多线程版本)、Other variants(其他的变种),三种实现如下图:
Reactor模式的详细介绍以及netty对其的具体实现
Reactor模式的详细介绍以及netty对其的具体实现
Reactor模式的详细介绍以及netty对其的具体实现
Netty的bossgroup与workgroup就是上图中的mainReactor和subReactor的具体实现。这里的Acceptor本质上是一个处理器,用来从mainReactor转发到subReactor中。netty把他放在了pipline的最后。
 在《Reactor》论文中对于Reactor的描述如下:
Reactor模式的详细介绍以及netty对其的具体实现
 虽然看起来Doug Lea的描述与上图不是很符合但是Reactor模式的本质都是一样的,上图的五种角色都可以在Doug Lea的文章中找到。
我们来介绍下Reactor模式的各个角色:
 1.handle(句柄):本质上是一种资源,是由操作系统提供的该资源用于事件,比如文件描述符或者是对网络编程中的socket描述符,事件既可以来自内部也可以来自外部,外部事件比如是客户端的连接请求,内部事件比如操作系统产生的定时器事件,本质就是一个文件描述符。他是事件产生的发源地,比如客户端连接过来产生一个read事件。
 2.Synchronous Event Demultiplexer(同步事件分离器):它本身是一个系统调用,用于等待事件的发生(可能是一个事件也可能是是多个),调用方在调用的时候会被堵塞,一直堵塞到同步事件分离器上有事件产生,对于Linux来说同步事件分离器就是I/O多路复用机制,如select,poll,epoll,它对应到java的NIO中就是selector。
 3.Event Handler(事件处理器):本身有多个回调方法构成,这些回调方法构成了与应用相关的对于某个事件的反馈机制。在java的NIO中没有定义这个事件处理器,这一部分是由程序开发者自己进行开发的,包括Concrete Event HandlerHandler都是由开发者自行实现,但是netty相较于NIO对用户比较友好的一点就是nettty中他对这个角色进行了升级,它定义了一些handler的接口来供开发者实现,在接口中它提供了大量的回调方法供我们在特定事件产生时实现相应的回调方法进行业务逻辑的处理。
 4.Concrete Event HandlerHandler(具体的事件处理器):从图中可以看出他是第三个组件的具体实现,他就是由程序开发这开发的具体的处理器,其中也是继承了回调方法来供开发者实现。
 5.Initiation Dispatcher(初始分发器):它实际上就是Doug Lea在文章中提到的Reactor角色。它本身定义了一些规范,这些规范用户控制事件的调度方式,同时提供了应用进行事件处理器的注册/删除(如上图)等操作。他是整个事件处理器的核心所在,Initiation Dispatcher会通过同步事件分离器来等待事件的发生,一旦事件发生,Initiation Dispatcher首先会分离出每一个事件然后调用事件处理器,最后调用相关的回调方法来处理这些事件。
接下来我们介绍下Reactor模式的流程:
 1.当应用向Initiation Dispatcher注册具体的事件处理器的时候,应用会标志出该事件处理器希望Initiation Dispatcher在某个事件发生时向其通知的该事件,该事件与handle相关联。(就是注册其感兴趣的事件)
 2.Initiation Dispatcher会要求每个事件向其传递内部的handle,该handle向操作系统标志了事件处理器。(就是每个事件处理器与一个handle相关联)。
 3.当所有的事件处理器注册完成以后,应用会调用handle_event方法来启动Initiation Dispatcher事件循环。这时,Initiation Dispatcher会 将每个注册事件管理器的handle合并起来,并且使用同步事件分离器等待这些事件的发生。比如说TCP协议层会使用select同步事件分离器操作来等待客户端发送的数据到达连接的socket handle上。
 4.当与某个事件源对应的handle变成ready状态的时候(比如TCP/socket变成等待读的状态),同步事件分离器就会通知Initiation Dispatcher。
 5.Initiation Dispatcher会触发事件处理器的回调方法,从而响应这个处于ready状态的handle。当事件发生时,Initiation Dispatcher会将被事件**的handle作为key来寻找并分发恰当的事件处理器回调方法。
 6.Initiation Dispatcher会回调事件处理器的handle_events回调方法来执行特定于应用的功能(开发者自己开发的),从而响应这个事件。所发生的事件类型可以作为该方法的参数并被该方法内部使用来执行二外的特定与服务的分离与分发。