Netty源码学习(2)-- Reactor模式

网络服务的基本服务:

读请求--> 解码请求 --> 处理服务器 --> 响应编码 --> 发送响应


传统的服务设计:

Netty源码学习(2)-- Reactor模式

每一个handler需要一个线程进行处理。所以当连接数特别大的时候,传统的方式会非常耗费资源。

Divide and Conquer

一种新的设计思路是将这一个大的任务变成多个小的任务,而Java中支持两种机制:Non-blocking读写和dispatch任务分发。所以我们可以考虑将一个大任务分解成多个非阻塞事件。比如我们可以使用read handler和write handler来处理读写事件,这样无论多少个链接,读写操作可以只用两个线程进行执行。


Reactor模式

reactor:通过分发给合适的处理器来响应IO事件,类似于AWT中的Thread(本质就是线程,netty中的EventLoop)

handlers:用来处理非阻塞操作,类似于AWT中的ActionListeners

管理:通过将handler绑定在事件上,类似于AWT中的addListener

Netty源码学习(2)-- Reactor模式

单线程react版本:

reactor这个线程不断调用selector.select()方法,当拿到一个准备好的事件key之后(SelectionKey),通过调用key.attachment()拿到这个事件上对应的一个runnable对象(以Acceptor为例), 调用acceptor方法,acceptor中的run方法将socket和channel传递给一个handler,由handler自己进行处理。

问题:单线程情况下worker的阻塞会直接导致reactor分发变慢,同时各个worker之间也会有相互影响。考虑将非IO处理移交给其他线程。对于reactor也能采用多线程模式。


多线程版本:

Netty源码学习(2)-- Reactor模式

在分发至handler中,handler通过线程池拿到线程进行处理。


多Reactor版本:

Netty源码学习(2)-- Reactor模式

mainReactor就是netty中的bossGroup,subReactor就是workerGroup,main只将事件分发给不同的sub,每个sub再链接相应的事件。


Reactor模拟的构成:

  1. handle(句柄描述符):本质上是一种资源,表示一个个事件,如文件描述符,针对网络编程中的socket描述符。事件既可以来自于外部,也可以来自于内部。linux下就是一个文件描述符。handle是事件产生的发源地。
  2. Synchronous Event Demultiplexer: 本身是一个系统调用,用于等待事件的发生,可能是一个,也可能是多个。调用时被阻塞,有事件才会继续。Linux中的select,poll,和epoll就是这种模式。对应NIO中的selector。
  3. EventHanlder: 本身由多个回调方法构成,这些回调方法构成了对某个事件的反馈。NIO中没有这个角色,但是netty进行了改进。
  4. Concrete Event handler:具体的事件处理器,用户一般继承eventHandler,实现回调方法,实现自己的功能。
  5. Initiation Dispatcher:就是eventLoop(Reactor),它定义了一些规范,规范用于控制事件的调度方式,同时又提供了应用进行事件处理的注册、删除等措施。它等待Synchronous Event Demultiplexer事件的发生,事件发生后,它会分离每一个事件,然后得到handler,调用其中的回调方法。

流程:

首先Initiation Dispatcher启动,并将handler进行注册,每个handler都会带一个感兴趣的事件handle。注册完毕后Synchronous Event Dispatcher启动,监听事件发生(select)。事件发生后拿到对应的handle,然后找到具体handler,调用其中对应的回调方法。


详解:

  1. 当应用向Initiation Dispatcher注册具体的事件处理器时(注册handler),同时回标出发生哪个事件时会调用这个handler,同时事件会和handle相关联。
  2. Initiation Dispatcher会要求处理器传递关联的handle。该handle向操作系统标识除了事件处理器。
  3. 当事件处理器注册完毕后,会调用handle_events方法来启动Initiation Dispatcher的事件循环。这时,Initiation Dispatcher会将每个事件管理器的handle合并起来,同时等待事件的发生。
  4. 当事件ready时,Synchronous Event Demultiplexer会通知Initiation Dispatcher。
  5. Initiation Dispatcher触发事件的回调方法,相应这个handle,拿到对应的handler,调用回调方法。


以客户都链接服务器,服务器打印客户端ip为例。首先新建一个Socket监听端口8899,这里的socket就是一个handle。接下来,我们新建一个handler,里面有一个函数接受handle(这里就是socket)作为参数,然后打印这个socket对应的ip。我们将handle和我们自己的handler进行关联,同时将handle注册到Initiation Dispatcher中,同时标注我们对connect事件感兴趣。然后Synchronous Event Demultiplexer会不断循环,知道有一个客户端进行了链接,连接之后我们Synchronous Event Demultiplexer通知Initiation Dispatcher这个事件(handle)ready了,同时因为这个handle有对应的handler,我们拿到handler,调用其中的方法,打印socket的ip即可。

本文出自知乎

Netty源码学习(2)-- Reactor模式

更多相关内容,Java架构师,软件开发等学习资料,电子书及视频还有高级讲师公开课免费资源

需要的可以私聊小编发送【学习】二字


分享到:


相關文章: