Netty3架构解析

简介:

前记

很早以前就有读Netty源码的打算了,然而第一次尝试的时候从Netty4开始,一直抓不到核心的框架流程,后来因为其他事情忙着就放下了。这次趁着休假重新捡起这个硬骨头,因为Netty3现在还在被很多项目使用,因而这次决定先从Netty3入手,瞬间发现Netty3的代码比Netty4中规中矩的多,很多概念在代码本身中都有清晰的表达,所以半天就把整个框架的骨架搞清楚了。再读 Netty4对Netty3的改进总结 ,回去读Netty4的源码,反而觉得轻松了,一种豁然开朗的感觉。

记得去年读Jetty源码的时候,因为代码太庞大,并且自己的HTTP Server的了解太少,因而只能自底向上的一个一个模块的叠加,直到最后把所以的模块连接在一起而看清它的真正核心骨架。现在读源码,开始习惯先把骨架理清,然后延伸到不同的器官、血肉而看清整个人体。

本文从Reactor模式在Netty3中的应用,引出Netty3的整体架构以及控制流程;然而除了Reactor模式,Netty3还在ChannelPipeline中使用了 Intercepting Filter 模式,这个模式也在Servlet的Filter中成功使用,因而本文还会从Intercepting Filter模式出发详细介绍ChannelPipeline的设计理念。本文假设读者已经对Netty有一定的了解,因而不会包含过多入门介绍,以及帮Netty做宣传的文字。

Netty3中的Reactor模式

Reactor模式在Netty中应用非常成功,因而它也是在Netty中受大肆宣传的模式,关于Reactor模式可以详细参考本人的另一篇文章 《Reactor模式详解》 ,对Reactor模式的实现是Netty3的基本骨架,因而本小节会详细介绍Reactor模式如何应用Netty3中。

如果读《Reactor模式详解》,我们知道Reactor模式由Handle、Synchronous Event Demultiplexer、Initiation Dispatcher、Event Handler、Concrete Event Handler构成,在Java的实现版本中,Channel对应Handle,Selector对应Synchronous Event Demultiplexer,并且Netty3还使用了两层Reactor:Main Reactor用于处理Client的连接请求,Sub Reactor用于处理和Client连接后的读写请求(关于这个概念还可以参考Doug Lea的这篇PPT: Scalable IO In Java )。所以我们先要解决Netty3中使用什么类实现所有的上述模块并把他们联系在一起的,以NIO实现方式为例:

模式是一种抽象,但是在实现中,经常会因为语言特性、框架和性能需要而做一些改变,因而Netty3对Reactor模式的实现有一套自己的设计:
1. ChannelEvent: Reactor是基于事件编程的,因而在Netty3中使用ChannelEvent抽象的表达Netty3内部可以产生的各种事件,所有这些事件对象在Channels帮助类中产生,并且由它将事件推入到ChannelPipeline中,ChannelPipeline构建ChannelHandler管道,ChannelEvent流经这个管道实现所有的业务逻辑处理。ChannelEvent对应的事件有:ChannelStateEvent表示Channel状态的变化事件,而如果当前Channel存在Parent Channel,则该事件还会传递到Parent Channel的ChannelPipeline中,如OPEN、BOUND、CONNECTED、INTEREST_OPS等,该事件可以在各种不同实现的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()方法来实现这种扩展,具体可以参考 这里
2. ChannelHandler: 在Netty3中,ChannelHandler用于表示Reactor模式中的EventHandler。ChannelHandler只是一个标记接口,它有两个子接口:ChannelDownstreamHandler和ChannelUpstreamHandler,其中ChannelDownstreamHandler表示从用户应用程序流向Netty3内部直到向Socket写数据的管道,在Netty4中改名为ChannelOutboundHandler;ChannelUpstreamHandler表示数据从Socket进入Netty3内部向用户应用程序做数据处理的管道,在Netty4中改名为ChannelInboundHandler。
3. ChannelPipeline: 用于管理ChannelHandler的管道,每个Channel一个ChannelPipeline实例,可以运行过程中动态的向这个管道中添加、删除ChannelHandler(由于实现的限制,在最末端的ChannelHandler向后添加或删除ChannelHandler不一定在当前执行流程中起效,参考 这里 )。ChannelPipeline内部维护一个ChannelHandler的双向链表,它以Upstream(Inbound)方向为正向,Downstream(Outbound)方向为方向。ChannelPipeline采用Intercepting Filter模式实现,具体可以参考 这里 ,这个模式的实现在后一节中还是详细介绍。
4. 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 的这张图表达:

若与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直接出发的原因。
6. Channel: Netty有自己的Channel抽象,它是一个资源的容器,包含了所有一个连接涉及到的所有资源的饮用,如封装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接口。

Netty3中的Intercepting Filter模式

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

关于Intercepting Filter模式的详细介绍可以参考 这里 ,本节主要介绍Netty3中对Intercepting Filter模式的实现,其实就是DefaultChannelPipeline对Intercepting Filter模式的实现。在上文有提到Netty3的ChannelPipeline是ChannelHandler的容器,用于存储与管理ChannelHandler,同时它在Netty3中也起到桥梁的作用,即它是连接Netty3内部到所有ChannelHandler的桥梁。作为ChannelPipeline的实现者DefaultChannelPipeline,它使用一个ChannelHandler的双向链表来存储,以DefaultChannelPipelineContext作为节点:
public   interface  ChannelHandlerContext {
    Channel getChannel();

    ChannelPipeline getPipeline();

    String getName();

    ChannelHandler getHandler();

    
boolean  canHandleUpstream();
    
boolean  canHandleDownstream();
    
void  sendUpstream(ChannelEvent e);
    
void  sendDownstream(ChannelEvent e);
    Object getAttachment();

    
void  setAttachment(Object attachment);
}

private   final   class  DefaultChannelHandlerContext  implements  ChannelHandlerContext {
    
volatile  DefaultChannelHandlerContext next;
    
volatile  DefaultChannelHandlerContext prev;
    
private   final  String name;
    
private   final  ChannelHandler handler;
    
private   final   boolean  canHandleUpstream;
    
private   final   boolean  canHandleDownstream;
    
private   volatile  Object attachment;
.....
}
在DefaultChannelPipeline中,它存储了和当前ChannelPipeline相关联的Channel、ChannelSink以及ChannelHandler链表的head、tail,所有ChannelEvent通过sendUpstream、sendDownstream为入口流经整个链表:
public   class  DefaultChannelPipeline  implements  ChannelPipeline {
    
private   volatile  Channel channel;
    
private   volatile  ChannelSink sink;
    
private   volatile  DefaultChannelHandlerContext head;
    
private   volatile  DefaultChannelHandlerContext tail;
......
    
public   void  sendUpstream(ChannelEvent e) {
        DefaultChannelHandlerContext head 
=  getActualUpstreamContext( this .head);
        
if  (head  ==   null ) {
            
return ;
        }
        sendUpstream(head, e);
    }

    
void  sendUpstream(DefaultChannelHandlerContext ctx, ChannelEvent e) {
        
try  {
            ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e);
        } 
catch  (Throwable t) {
            notifyHandlerException(e, t);
        }
    }

    
public   void  sendDownstream(ChannelEvent e) {
        DefaultChannelHandlerContext tail 
=  getActualDownstreamContext( this .tail);
        
if  (tail  ==   null ) {
            
try  {
                getSink().eventSunk(
this , e);
                
return ;
            } 
catch  (Throwable t) {
                notifyHandlerException(e, t);
                
return ;
            }
        }
        sendDownstream(tail, 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) {
            e.getFuture().setFailure(t);
            notifyHandlerException(e, t);
        }
    }
对Upstream事件,向后找到所有实现了ChannelUpstreamHandler接口的ChannelHandler组成链( getActualUpstreamContext()) ,而对Downstream事件,向前找到所有实现了ChannelDownstreamHandler接口的ChannelHandler组成链( getActualDownstreamContext() ):
     private  DefaultChannelHandlerContext getActualUpstreamContext(DefaultChannelHandlerContext ctx) {
        
if  (ctx  ==   null ) {
            
return   null ;
        }
        DefaultChannelHandlerContext realCtx 
=  ctx;
        
while  ( ! realCtx.canHandleUpstream()) {
            realCtx 
=  realCtx.next;
            
if  (realCtx  ==   null ) {
                
return   null ;
            }
        }
        
return  realCtx;
    }
    
private  DefaultChannelHandlerContext getActualDownstreamContext(DefaultChannelHandlerContext ctx) {
        
if  (ctx  ==   null ) {
            
return   null ;
        }
        DefaultChannelHandlerContext realCtx 
=  ctx;
        
while  ( ! realCtx.canHandleDownstream()) {
            realCtx 
=  realCtx.prev;
            
if  (realCtx  ==   null ) {
                
return   null ;
            }
        }
        
return  realCtx;
    }
在实际实现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);
    }
}
当ChannelHandler向ChannelPipelineContext发送事件时,其内部从当前ChannelPipelineContext节点出发找到下一个ChannelUpstreamHandler或ChannelDownstreamHandler实例,并向其发送ChannelEvent,对于Downstream链,如果到达链尾,则将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);
    }
}

public   void  sendUpstream(ChannelEvent e) {
    DefaultChannelHandlerContext next 
=  getActualUpstreamContext( this .next);
    
if  (next  !=   null ) {
        DefaultChannelPipeline.
this .sendUpstream(next, e);
    }
}
正是因为这个实现,如果在一个末尾的ChannelUpstreamHandler中先移除自己,在向末尾添加一个新的ChannelUpstreamHandler,它是无效的,因为它的next已经在调用前就固定设置为null了。

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);

    
< extends  ChannelHandler >  T remove(Class < T >  handlerType);
    ChannelHandler removeFirst();

    ChannelHandler removeLast();

    
void  replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler);
    ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler);

    
< extends  ChannelHandler >  T replace(Class < T >  oldHandlerType, String newName, ChannelHandler newHandler);
    ChannelHandler getFirst();

    ChannelHandler getLast();

    ChannelHandler get(String name);

    
< extends  ChannelHandler >  T get(Class < T >  handlerType);
    ChannelHandlerContext getContext(ChannelHandler handler);

    ChannelHandlerContext getContext(String name);

    ChannelHandlerContext getContext(Class
<?   extends  ChannelHandler >  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
< String >  getNames();
    Map
< String, ChannelHandler >  toMap();
}

在DefaultChannelPipeline的ChannelHandler链条的处理流程为:


相关文章
|
10天前
|
Linux 编译器 开发者
Linux设备树解析:桥接硬件与操作系统的关键架构
在探索Linux的庞大和复杂世界时🌌,我们经常会遇到许多关键概念和工具🛠️,它们使得Linux成为了一个强大和灵活的操作系统💪。其中,"设备树"(Device Tree)是一个不可或缺的部分🌲,尤其是在嵌入式系统🖥️和多平台硬件支持方面🔌。让我们深入了解Linux设备树是什么,它的起源,以及为什么Linux需要它🌳。
Linux设备树解析:桥接硬件与操作系统的关键架构
|
1月前
|
存储 搜索推荐 数据挖掘
ElasticSearch架构介绍及原理解析
ElasticSearch架构介绍及原理解析
84 0
|
19天前
|
canal 消息中间件 关系型数据库
【分布式技术专题】「分布式技术架构」MySQL数据同步到Elasticsearch之N种方案解析,实现高效数据同步
【分布式技术专题】「分布式技术架构」MySQL数据同步到Elasticsearch之N种方案解析,实现高效数据同步
66 0
|
24天前
|
运维 Linux Apache
LAMP架构调优(十)——Apache禁止指定目录PHP解析与错误页面优化
LAMP架构调优(十)——Apache禁止指定目录PHP解析与错误页面优化
196 2
|
28天前
|
消息中间件 弹性计算 Kubernetes
Knative 架构解析
【2月更文挑战第29天】Knative作为构建无服务器产品的基础设施,建立在Kubernetes和Istio之上,提供从源代码到服务的编排、流量管理、自动扩缩容和事件绑定等功能,分为Build、Eventing和Serving三个模块,旨在确保编程模型的可移植性。
|
29天前
|
设计模式 存储 API
C++桥接模式大解析:轻松设计与实现高效软件架构
C++桥接模式大解析:轻松设计与实现高效软件架构
159 0
|
1月前
|
消息中间件 存储 Cloud Native
深度剖析 RocketMQ 5.0,架构解析:云原生架构如何支撑多元化场景?
了解 RocketMQ 5.0 的核心概念和架构概览;然后我们会从集群角度出发,从宏观视角学习 RocketMQ 的管控链路、数据链路、客户端和服务端如何交互;学习 RocketMQ 如何实现数据的存储,数据的高可用,如何利用云原生存储进一步提升竞争力。
140004 0
|
7月前
|
监控 Java Linux
由浅入深Netty基础知识NIO网络编程1
由浅入深Netty基础知识NIO网络编程
38 0
|
7月前
|
缓存 安全 Java
由浅入深Netty基础知识NIO三大组件原理实战 2
由浅入深Netty基础知识NIO三大组件原理实战
44 0
|
7月前
|
Java
由浅入深Netty基础知识NIO三大组件原理实战 1
由浅入深Netty基础知识NIO三大组件原理实战
58 0

推荐镜像

更多