Netty架构分析

Netty架构解析

Netty中Reactor模式

前面分析了Reactor模式,下面来详细介绍Netty通过哪些类组件、怎样的类之间关系来实现Reactor模式的。

下面为netty中涉及到的类,以及类与类之间的关系,下面会将各个类角色和Reactor模式中结合起来理解。

Netty架构分析_第1张图片

图-netty-class-channel

1、Channel

Channel实现类中会包含NIO Channel,这个才是Reactor模式中的句柄,将感兴趣的事件注册到selector中进行监听

其实Channel是Netty的抽象向应用程序屏蔽了具体实现,它是一个资源的容器,包含了所有一个连接涉及到的所有资源的引用,如封装java NIO Channel、ChannelPipeline、Boss、NioWorkerPool等。另外它还提供了向内部NIO Channel写响应数据的接口write、连接/绑定到某个地址的connect/bind接口等,个人感觉虽然对Channel本身来说,因为它封装了NIO Channel,因而这些接口定义在这里是合理的,但是如果考虑到Netty的架构,它的Channel只是一个资源容器,有这个Channel实例就可以得到和它相关的基本所有资源,因而这种write、connect、bind动作不应该再由它负责,而是应该由其他类来负责,比如在Netty4中就在ChannelHandlerContext添加了write方法,虽然netty4并没有删除Channel中的write接口。

2、 ChannelPipeline

用于管理ChannelHandler的管道,每个Channel一个ChannelPipeline实例,可以运行过程中动态的向这个管道中添加、删除ChannelHandler(由于实现的限制,在最末端的ChannelHandler向后添加或删除ChannelHandler不一定在当前执行流程中起效)。ChannelPipeline内部维护一个ChannelHandler的双向链表,它以Upstream(Inbound)方向为正向,Downstream(Outbound)方向为方向。ChannelPipeline采用Intercepting Filter模式实现,这个模式的实现在后一节中还是详细介绍。

3、ChannelHandler

Reactor模式中的Event Handler, 其中ChannelHandler有两个子接口 ChannelUpstreamHandler ChannelDownstreamHandler

ChannelUpstreamHandler 从Socket进入Netty内部向用户应用程序做数据处理,一般为新消息接受、Channel状态被动改变(已经绑定成功、已经连接成功、channel被关闭) Netty4叫做 ChannelInboundHandler

ChannelDownstreamHandler 从用户程序流经Netty内部,最终向Sockt写入数据,一般为Channel状态主动改变(将要去绑定某个端口、去连接服务端)、消息写入 Netty4叫做ChannelOutboundHandler

4、ChannelEvent

Reactor模式是基于事件编程的,可在Channels帮助类产生,并将事件通过ChannelPipeline流经所有业务逻辑处理Handler, Channel事件的抽象,例如消息事件、异常事件、状态事件都是基于此接口扩展的。
ChannelStateEvent: 代表Channel状态发生变化,如果Channel 的parent channel不为空则还会传递到parent channel的ChannelPipeline中,如: BOUND OPEN CLOSE CONNECTED 这种事件会在各种不同的Channel实现 和ChannelSink中产生。

MessageEvent表示从Socket中读取数据完成、需要向Socket写数据或ChannelHandler对当前Message解析(如Decoder、Encoder)后触发的事件,它由NioWorker、需要对Message做进一步处理的ChannelHandler产生;

WriteCompletionEvent表示写完成而触发的事件,它由NioWorker产生;ExceptionEvent表示在处理过程中出现的Exception,它可以发生在各个构件中,如Channel、ChannelSink、NioWorker、ChannelHandler中;

IdleStateEvent由IdleStateHandler触发,这也是一个ChannelEvent可以无缝扩展的例子。注:在Netty4后,已经没有ChannelEvent类,所有不同事件都用对应方法表达,这也意味这ChannelEvent不可扩展,Netty4采用在ChannelInboundHandler中加入userEventTriggered()方法来实现这种扩展

5、NioSelector

Netty3 使用NioSelector来存放Selector (Synchronous Event Demultiplexer)监听注册的感兴趣句柄的事件,每个新产生的NIO Channel都向这个Selector注册自己以让这个Selector监听这个NIO Channel中发生的事件,当事件发生时,调用帮助类Channels中的方法生成ChannelEvent实例,将该事件发送到这个Netty Channel对应的ChannelPipeline中,而交给各级ChannelHandler处理。其中在向Selector注册NIO Channel时,Netty Channel实例以Attachment的形式传入,该Netty Channel在其内部的NIO Channel事件发生时,会以Attachment的形式存在于SelectionKey中,因而每个事件可以直接从这个Attachment中获取相关链的Netty Channel,并从Netty Channel中获取与之相关联的ChannelPipeline,这个实现和Doug Lea的Scalable IO In Java一模一样。另外Netty3还采用了Scalable IO In Java中相同的Main Reactor和Sub Reactor设计,其中NioSelector的两个实现:Boss即为Main Reactor,NioWorker为Sub Reactor。Boss用来处理新连接加入的事件,NioWorker用来处理各个连接对Socket的读写事件,其中Boss通过NioWorkerPool获取NioWorker实例,Netty3模式使用RoundRobin方式放回NioWorker实例。更形象一点的,可以通过Scalable IO In Java的这张图表达:

Netty架构分析_第2张图片

图-netty-multi-reactor

若与Ractor模式对应,NioSelector中包含了Synchronous Event Demultiplexer,而ChannelPipeline中管理着所有EventHandler,因而NioSelector和ChannelPipeline共同构成了Initiation Dispatcher。

5、ChannelSink

在ChannelHandler处理完成所有逻辑需要向客户端写响应数据时,一般会调用Netty Channel中的write方法,然而在这个write方法实现中,它不是直接向其内部的Socket写数据,而是交给Channels帮助类,内部创建DownstreamMessageEvent,反向从ChannelPipeline的管道中流过去,直到第一个ChannelHandler处理完毕,最后交给ChannelSink处理,以避免阻塞写而影响程序的吞吐量。ChannelSink将这个MessageEvent提交给Netty Channel中的writeBufferQueue,最后NioWorker会等到这个NIO Channel已经可以处理写事件时无阻塞的向这个NIO Channel写数据。这就是上图的send是从SubReactor直接出发的原因。

Netty3中的Intercepting Filter(拦截器+责任链)模式

如果说Reactor模式是Netty3的骨架,那么Intercepting Filter模式则是Netty的中枢。Reactor模式主要应用在Netty3的内部实现,它是Netty3具有良好性能的基础,而Intercepting Filter模式则是ChannelHandler组合实现一个应用程序逻辑的基础,只有很好的理解了这个模式才能使用好Netty,甚至能得心应手。

在上文有提到Netty3的ChannelPipeline是ChannelHandler的容器,用于存储与管理ChannelHandler,同时它在Netty3中也起到桥梁的作用,即它是连接Netty3内部到所有ChannelHandler的桥梁。作为ChannelPipeline的实现者DefaultChannelPipeline,它使用一个ChannelHandlerContext的双向链表来存储,以DefaultChannelPipelineContext作为节点:

ChannelPipeline内部实现

DefaultChannelPipeline实现了ChannelPipeline接口,其类中包含以下属性

属性:

Channel channel;
ChannelSink sink;
DefaultChannelHandlerContext head;
DefaultChannelHandlerContext tail;
Map name2ctx = new HashMap(4);

其中:ChannelHandlerContext保存了Netty与Handler相关上下文信息,其中DefaultChannelHandlerContext中除了包含ChannelHandler以外还有包含 next和prev两个指针指向前一个和后一个DefaultChannelHandlerContext,从而形成一个双向链表。

head和tail就是链表的头和尾

name2ctx维持了一个ChannelHandler 名字和Context映射关系,可以按照名字删除或添加

方法:

//该方法是所有handler处理的源头流入整个整个链表,想要触发后续的handler必须调用该方法
public void sendDownstream(ChannelEvent e){
//从链表头往后找实现ChannelUpstreamHandler接口的ChannelHandler
DefaultChannelHandlerContext head = getActualUpstreamContext(this.next);

sendUpstreamHandler(head,e)

}

public void sendUpstream(ChannelEvent e){
//从链表尾往头部找实现ChannelDownstreamHandler接口的ChannelHandler
 DefaultChannelHandlerContext tail = getActualDownstreamContext(this.tail);
//首选判断 tail是否为null 
 if(tail == null){
  //调用ChannelSink处理事件
  getSink().eventSunk(this,e);
  return;
 }catch (Throwable t) {
                notifyHandlerException(e, t);
                return;
            }
        }
        sendDownstream(tail, e);


}


public void sendUpstream(ChannelHandlerContext ctx,ChannelEvent e){
//大致逻辑
(ChannelUpstreamHandler)ctx.getHandler()).handlerUpstream(ctx,e);

}

void sendDownstream(DefaultChannelHandlerContext ctx, ChannelEvent e) {
        if (e instanceof UpstreamMessageEvent) {
            throw new IllegalArgumentException("cannot send an upstream event to downstream");
        }

    try {
        ((ChannelDownstreamHandler) ctx.getHandler()).handleDownstream(ctx, e);
    } catch (Throwable t) {
        // Unlike an upstream event, a downstream event usually has an
        // incomplete future which is supposed to be updated by ChannelSink.
        // However, if an exception is raised before the event reaches at
        // ChannelSink, the future is not going to be updated, so we update
        // here.
        e.getFuture().setFailure(t);
        notifyHandlerException(e, t);
    }
}

DefaultChannelPipeline内部类:

在实际实现ChannelUpstreamHandler或ChannelDownstreamHandler时,调用 ChannelHandlerContext中的sendUpstream或sendDownstream方法将控制流程交给下一个 ChannelUpstreamHandler或下一个ChannelDownstreamHandler,或调用Channel中的write方法发送响应消息

public class MyChannelUpstreamHandler implements ChannelUpstreamHandler {
    public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
        // handle current logic, use Channel to write response if needed.
        // ctx.getChannel().write(message);
        ctx.sendUpstream(e);
    }
}

public class MyChannelDownstreamHandler implements ChannelDownstreamHandler {
    public void handleDownstream(
            ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
        // handle current logic
        ctx.sendDownstream(e);
    }
}


private class DefaultChannelHandlerContext implements ChannelHandlerContext{

  DefaultChannelHandlerContext next;
  DefaultChannelHandlerContext prev;
  String name;
  ChannelHandler handler;
  
  //该方法是在具体的Handler中处理完逻辑后进行显示的调用 ctx.sendUpstreamHandler(e)方法将事件传递到下个handler进行处理
  public void sendUpstream(ChannelEvent e){
  
  //从链表中获取UpstreamHandler	
  DefaultChannelHandlerContext next = getActualUpstreamContex(this.next);
  
  //实际上还是调用了piple类中的 sendUpstreamHandler方法
  DefaultChannelPipeline.this.sendUpstreamHandler(this.next,e);
  
  }
  
  //如果到达链尾,则将ChannelEvent发送给ChannelSink
  public void sendDownstream(ChannelEvent e) {
DefaultChannelHandlerContext prev = getActualDownstreamContext(this.prev);
if (prev == null) {
    try {
        getSink().eventSunk(DefaultChannelPipeline.this, e);
    } catch (Throwable t) {
        notifyHandlerException(e, t);
    }
} else {
    DefaultChannelPipeline.this.sendDownstream(prev, e);
}
}

}

正是由于这种实现,如果在一个末尾的ChannelUpstreamHandler first中先移除自己,在向末尾添加一个新的ChannelUpstreamHandler second,它是无效的,因为first的next已经在调用前就固定设置为null了。所以再调用 ctx.sendUpstream(e) 方法时去找first的下一个结点,发现为null所以后面的second Handler就不会执行了

解决方法: 在remove frist之前调用 pipeline.addLast(handler) 或者在first 和 second之间存在至少一个ChannelHandler,也就是 first不是最后一个ChannelHandler

ChannelPipeline作为ChannelHandler的容器,它还提供了各种增、删、改ChannelHandler链表中的方法,而且如果某个ChannelHandler还实现了LifeCycleAwareChannelHandler,则该ChannelHandler在被添加进ChannelPipeline或从中删除时都会得到通知:

public interface LifeCycleAwareChannelHandler extends ChannelHandler {
    void beforeAdd(ChannelHandlerContext ctx) throws Exception;
    void afterAdd(ChannelHandlerContext ctx) throws Exception;
    void beforeRemove(ChannelHandlerContext ctx) throws Exception;
    void afterRemove(ChannelHandlerContext ctx) throws Exception;
}

public interface ChannelPipeline {
    void addFirst(String name, ChannelHandler handler);
    void addLast(String name, ChannelHandler handler);
    void addBefore(String baseName, String name, ChannelHandler handler);
    void addAfter(String baseName, String name, ChannelHandler handler);
    void remove(ChannelHandler handler);
    ChannelHandler remove(String name);
     T remove(Class handlerType);
    ChannelHandler removeFirst();
    ChannelHandler removeLast();
    void replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler);
    ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler);
     T replace(Class oldHandlerType, String newName, ChannelHandler newHandler);
    ChannelHandler getFirst();
    ChannelHandler getLast();
    ChannelHandler get(String name);
     T get(Class handlerType);
    ChannelHandlerContext getContext(ChannelHandler handler);
    ChannelHandlerContext getContext(String name);
    ChannelHandlerContext getContext(Class handlerType);
    void sendUpstream(ChannelEvent e);
    void sendDownstream(ChannelEvent e);
    ChannelFuture execute(Runnable task);
    Channel getChannel();
    ChannelSink getSink();
    void attach(Channel channel, ChannelSink sink);
    boolean isAttached();
    List getNames();
    Map toMap();
}

Netty架构分析_第3张图片

图-netty-channelpipeline

在ChannelHandler可能会通过 ctx.getChannel.write(msg) 向Channel中写入数据,这个时候就从UpstreamHandler链 转到 DownstreamHandler链进行处理

Netty架构分析_第4张图片

图-netty-channelpipeline-flow

该图直接从源码中复制的,不过在下游事件中增加了一个ChannelSink,这个是所以Downstream Event必经的一个组件,主要就是将message写入Socket中,将数据传输到网络中。 当然还包括一些对NIO Channel操作,例如bind connect close Channel事件也都会进入到ChannelSink,然后继续将事件委托给其他的类处理(NioWorker或Boss)

Netty架构分析_第5张图片

图-netty-channel-flow

可很直观看到ChannelEvent通过ChannelPipeline流经各个ChannelHandler进行逻辑处理。
后面我会对各几大模块(引导类、ChannelPipeline、Channel、I/O模型)用具体代码深入理解Netty的实现。

我从Netty3揭开背后的面纱的原因是:Netty4、5的引用的Reactor模式基本都一样,只要把Netty3摸清楚了,对于新版本的也是有很大作用的。更多的是,通过阅读Netty3源码就更加深入理解Netty4中的某些特性。还有更能看到Netty3存在哪些性能或其他问题,在Netty4是如何解决的,有助于把某些好的点子延伸到我们的项目中。
例如:在Netty3中,同个Channel中的下游ChannelHandler中如果没有实现同步的话,就会导致线程安全问题,因为有可能多个线程同时往Channel写入message,那么就会出现线程并发经过DownstreamChannelHandler,由于上游消息事件是由I/O线程来触发的,一个Channel对应于一个线程,所以当前事件处理结束之前就不会有下个事件。在Netty4中,就对此做了优化,将出站事件和入站事件都交给一开始和Channel绑定的EventLoop进行处理(I/O线程)

1、为了避免线程的切换(例如在上游处理过程中产生了异常,那么就会下发一个ExceptionDownstreamEvent交给DownstreamChannelHandler来处理最后会由I/O线程进行继续产生上游异常事件,进而导致线程的切换)

2、避免线程的同步

并且我会把Netty3以及Netty4相关的代码笔记上传到代码库中,以供大家参考Netty源码笔记。

你可能感兴趣的:(Netty原理剖析,Netty内部实现原理剖析,Netty架构,源码,Channel,Selector,Reactor)