本文共 5312 字,大约阅读时间需要 17 分钟。
对于传统的c/s服务模式,当客户端有请求到达服务端,服务端会有对应的线程去处理数据间的交互。当客户端请求数量很大的时候,服务端会有很多的线程来处理,此时线程间的切换会耗费巨大的资源,同时线程本身也会消耗很多的资源。当连接没有数据传输的时候,线程仍然需要监听连接,此时也会造成资源的无效浪费。因此,reactor模式可以很好的来解决出现的这些问题。
reactor: An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events
Handle
handle称为句柄或是描述符,本质上表示一种资源,是由操作系统提供的。该资源用于表示一个个的事件,比如说文件描述符,针对网络变成中的Socket描述符。事件既可以来自外部,也可以来自于内部。外部事件比如说客户端的连接请求等;内部事件比如说操作系统产生的定时器事件等。它本质上就是一个文件描述符(在linux系统中一切都是文件)。Handle是事件产生的发源地。Sysnchronous Event Demultiplexer
Sysnchronous Event Demultiplexer称为同步事件分离器,它本身是由系统调用用于等待事件的发生(事件可能是一个,也可能是多个)。调用方在调用它的时候会被阻塞,直到同步事件分离器上有事件产生为止。对于Linux来说,同步事件分离器指的就是常用的I/O多路复用机制,比如说select、epoll、epoll等。在Java nio领域中,同步事件分离器对应的组件就是Selector,对应的阻塞方法就是select方法。Event Handler
Event Handler称为事件处理器,由多个回调方法构成,这些回调方法构成了与应用相关的对于某个事件的反馈机制。在Java nio领域中,是没有这一个角色的,某个事件产生,我们在代码中自己判断事件类型并且去处理相应的逻辑。Netty相对于Java nio来说,在事件处理器这个角色上进行来一个升级,它为我们开发者提供了大量的回调方法,供我们在特定事件产生时实现相应的回调方法进行业务逻辑的处理。例如ChannelInboundHandlerAdapter类,定义来一系列回调的方法供我们在相应事件发生的时候来调用。Concrete Event Handler
Concrete Event Handler具体事件处理器,是事件处理器的实现。实现了事件处理器所提供的各个回调方法,从而实现来特定于业务的逻辑。本质就是我们所编写的一个个的处理器实现。public abstract class SimpleChannelInboundHandler extends ChannelInboundHandlerAdapter { ... protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;...}public class MyServerHandler extends SimpleChannelInboundHandler{ @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(ctx.channel().remoteAddress() + "," + msg); ctx.channel().writeAndFlush("from server: " + UUID.randomUUID()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}
MyServerHandler就是具体的事件处理器。实现了channelRead0方法,该方法定义于父类也就是事件处理器中。
5. Initiation Dispatcher Initiation Dispatcher称为初始化分发器,实际上就是Reactor角色。定义来一些规范,这些规范用于控制事件的调度方式(Initiation Dispatcher拿到select返回的selectionkey结果集),同时又提供了应用进行事件处理器的注册、删除等设施(下图中的register_handler(h)/remove_handler(h))。此组件是整个事件处理器的核心所在,Initiation Dispatcher会通过同步事件分离器来等待事件的发生,一旦事件发生来,Initiation Dispatcher首先分离出每一个事件(遍历selectionkey结果集,每一个元素就是一个事件),然后调用事件处理器,最后会调用相关的回调方法来处理这些事件。参考以下权威的两张图:
上述两个图的一些差异。在netty中,有对应的mainReactor(BossGroup)和subReactor(WorkGroup),两者就是Initiation Dispatcher。mainReactor只会接受连接,并传递给subReactor,因此中间的acceptor就是启动连接的作用。netty在服务器绑定到端口号上的时候,会向mainReactor中添加一个handler也就是一个ChannelInitializer(一个inbound类型的handler),在这里新建一个ServerBootstrapAcceptor对象,每次绑定新的端口都会产生这个组件。源码中也就是添加这个handler到pipeline中的最后位置。当acceptor接受到客户端传递的数据之后,会在channelRead方法中将原客户端的channel绑定到subReactor(io线程组)中。
void init(Channel channel) throws Exception { ... ChannelPipeline p = channel.pipeline(); ... p.addLast(new ChannelInitializer() { @Override public void initChannel(final Channel ch) throws Exception { final ChannelPipeline pipeline = ch.pipeline(); ChannelHandler handler = config.handler(); if (handler != null) { pipeline.addLast(handler); } ch.eventLoop().execute(new Runnable() { @Override public void run() { pipeline.addLast(new ServerBootstrapAcceptor( ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); } }); } }); }
ServerBootstrapAcceptor类中:
public void channelRead(ChannelHandlerContext ctx, Object msg) { final Channel child = (Channel) msg; child.pipeline().addLast(childHandler); setChannelOptions(child, childOptions, logger); for (Entry, Object> e: childAttrs) { child.attr((AttributeKey