Netty 简介之核心组件

1.1 Netty 核心组件

为了后期更好地理解和进一步深入 Netty,有必要总体认识一下 Netty 所用到的核心组件以及他们在整个 Netty 架构中是如何协调工作的。

Netty 有如下几个核心组件:

Bootstrap & ServerBootstrap

Channel

ChannelFuture

EventLoop & EventLoopGroup

ChannelHandler

ChannelPipeline

核心组件的高层类图如下:

1

2.1 Bootstrap & ServerBootstrap

这 2 个类都继承了AbstractBootstrap,因此它们有很多相同的方法和职责。它们都是启动器,能够帮助 Netty 使用者更加方便地组装和配置 Netty ,也可以更方便地启动 Netty 应用程序。相比使用者自己从头去将 Netty 的各部分组装起来要方便得多,降低了使用者的学习和使用成本。它们是我们使用 Netty 的入口和最重要的 API ,可以通过它来连接到一个主机和端口上,也可以通过它来绑定到一个本地的端口上。总的来说,它们两者之间相同之处要大于不同

 Bootstrap & ServerBootstrap 对于 Netty ,就相当于 Spring Boot 是 Spring 的启动器。

它们和其它组件之间的关系是它们将 Netty 的其它组件进行组装和配置,所以它们会组合和直接或间接依赖其它的类。

Bootstrap 用于启动一个 Netty TCP 客户端,或者 UDP 的一端。

通常使用 #connet(...) 方法连接到远程的主机和端口,作为一个 Netty TCP 客户端。

也可以通过 #bind(...) 方法绑定本地的一个端口,作为 UDP 的一端。

仅仅需要使用一个 EventLoopGroup 。

ServerBootstrap 往往是用于启动一个 Netty 服务端。

通常使用 #bind(...) 方法绑定本地的端口上,然后等待客户端的连接。

使用两个 EventLoopGroup 对象( 当然这个对象可以引用同一个对象 ):第一个用于处理它本地 Socket 连接的 IO 事件处理,而第二个责负责处理远程客户端的 IO 事件处理。

2.2 Channel

Channel 是 Netty 网络操作抽象类,它除了包括基本的 I/O 操作,如 bind、connect、read、write 之外,还包括了 Netty 框架相关的一些功能,如获取该 Channel 的 EventLoop 。

在传统的网络编程中,作为核心类的 Socket ,它对程序员来说并不是那么友好,直接使用其成本还是稍微高了点。而 Netty 的 Channel 则提供的一系列的 API ,它大大降低了直接与 Socket 进行操作的复杂性。而相对于原生 NIO 的 Channel,Netty 的 Channel 具有如下优势( 摘自《Netty权威指南( 第二版 )》) :

在 Channel 接口层,采用 Facade 模式进行统一封装,将网络 I/O 操作、网络 I/O 相关联的其他操作封装起来,统一对外提供。

Channel 接口的定义尽量大而全,为 SocketChannel 和 ServerSocketChannel 提供统一的视图,由不同子类实现不同的功能,公共功能在抽象父类中实现,最大程度地实现功能和接口的重用。

具体实现采用聚合而非包含的方式,将相关的功能类聚合在 Channel 中,由 Channel 统一负责和调度,功能实现更加灵活。

2.2 EventLoop && EventLoopGroup

Netty 基于事件驱动模型,使用不同的事件来通知我们状态的改变或者操作状态的改变。它定义了在整个连接的生命周期里当有事件发生的时候处理的核心抽象。

Channel 为Netty 网络操作抽象类,EventLoop 负责处理注册到其上的 Channel 处理 I/O 操作,两者配合参与 I/O 操作。

EventLoopGroup 是一个 EventLoop 的分组,它可以获取到一个或者多个 EventLoop 对象,因此它提供了迭代出 EventLoop 对象的方法。

下图是 Channel、EventLoop、Thread、EventLoopGroup 之间的关系( 摘自《Netty In Action》) :

Channel、EventLoop、Thread、EventLoopGroup

一个 EventLoopGroup 包含一个或多个 EventLoop ,即 EventLoopGroup : EventLoop = 1 : n 。

一个 EventLoop 在它的生命周期内,只能与一个 Thread 绑定,即 EventLoop : Thread = 1 : 1 。

所有有 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理,从而保证线程安全,即 Thread : EventLoop = 1 : 1。

一个 Channel 在它的生命周期内只能注册到一个 EventLoop 上,即 Channel : EventLoop = n : 1 。

一个 EventLoop 可被分配至一个或多个 Channel ,即 EventLoop : Channel = 1 : n 。

当一个连接到达时,Netty 就会创建一个 Channel,然后从 EventLoopGroup 中分配一个 EventLoop 来给这个 Channel 绑定上,在该 Channel 的整个生命周期中都是有这个绑定的 EventLoop 来服务的。

2.4 ChannelFuture

Netty 为异步非阻塞,即所有的 I/O 操作都为异步的,因此,我们不能立刻得知消息是否已经被处理了。Netty 提供了 ChannelFuture 接口,通过该接口的 #addListener(...) 方法,注册一个 ChannelFutureListener,当操作执行成功或者失败时,监听就会自动触发返回结果。

2.5 ChannelHandler

ChannelHandler ,连接通道处理器,我们使用 Netty 中最常用的组件。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。

ChannelHandler 有两个核心子类 ChannelInboundHandler 和 ChannelOutboundHandler,其中 ChannelInboundHandler 用于接收、处理入站( Inbound )的数据和事件,而 ChannelOutboundHandler 则相反,用于接收、处理出站( Outbound )的数据和事件。

ChannelInboundHandler 的实现类还包括一系列的 Decoder 类,对输入字节流进行解码。

ChannelOutboundHandler 的实现类还包括一系列的 Encoder 类,对输入字节流进行编码。

ChannelDuplexHandler 可以同时用于接收、处理入站和出站的数据和时间。

ChannelHandler 还有其它的一系列的抽象实现 Adapter ,以及一些用于编解码具体协议的 ChannelHandler 实现类。

2.6 ChannelPipeline

ChannelPipeline 为 ChannelHandler 的,提供了一个容器并定义了用于沿着链传播入站和出站事件流的 API 。一个数据或者事件可能会被多个 Handler 处理,在这个过程中,数据或者事件经流 ChannelPipeline ,由 ChannelHandler 处理。在这个处理过程中,一个 ChannelHandler 接收数据后处理完成后交给下一个 ChannelHandler,或者什么都不做直接交给下一个 ChannelHandler。

ChannelPipeline

当一个数据流进入 ChannelPipeline 时,它会从 ChannelPipeline 头部开始,传给第一个 ChannelInboundHandler 。当第一个处理完后再传给下一个,一直传递到管道的尾部。

与之相对应的是,当数据被写出时,它会从管道的尾部开始,先经过管道尾部的“最后”一个ChannelOutboundHandler ,当它处理完成后会传递给前一个 ChannelOutboundHandler 。

上图更详细的,可以是如下过程:

*                                                I/O Request

*                                            via {@linkChannel} or

*                                        {@linkChannelHandlerContext}

*                                                      |

*  +---------------------------------------------------+---------------+

*  |                          ChannelPipeline        |              |

*  |                                                  \|/              |

*  |    +---------------------+            +-----------+----------+    |

*  |    | Inbound Handler  N  |            | Outbound Handler1|    |

*  |    +----------+----------+            +-----------+----------+    |

*  |              /|\                                  |              |

*  |              |                                  \|/              |

*  |    +----------+----------+            +-----------+----------+    |

*  |    | Inbound Handler N-1|            | Outbound Handler2|    |

*  |    +----------+----------+            +-----------+----------+    |

*  |              /|\                                  .              |

*  |              .                                  .              |

*  | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|

*  |        [ method call]                      [method call]        |

*  |              .                                  .              |

*  |              .                                  \|/              |

*  |    +----------+----------+            +-----------+----------+    |

*  |    | Inbound Handler2|            | Outbound Handler M-1|    |

*  |    +----------+----------+            +-----------+----------+    |

*  |              /|\                                  |              |

*  |              |                                  \|/              |

*  |    +----------+----------+            +-----------+----------+    |

*  |    | Inbound Handler1|            | Outbound Handler  M  |    |

*  |    +----------+----------+            +-----------+----------+    |

*  |              /|\                                  |              |

*  +---------------+-----------------------------------+---------------+

*                  |                                  \|/

*  +---------------+-----------------------------------+---------------+

*  |              |                                  |              |

*  |      [ Socket.read() ]                    [ Socket.write() ]    |

*  |                                                                  |

*  |  Netty Internal I/OThreads(Transport Implementation)|

*  +-------------------------------------------------------------------+

当 ChannelHandler 被添加到 ChannelPipeline 时,它将会被分配一个 ChannelHandlerContext ,它代表了 ChannelHandler 和 ChannelPipeline 之间的绑定。其中 ChannelHandler 添加到 ChannelPipeline 中,通过 ChannelInitializer 来实现,过程如下:

一个 ChannelInitializer 的实现对象,被设置到了 BootStrap 或 ServerBootStrap 中。

当 ChannelInitializer#initChannel() 方法被调用时,ChannelInitializer 将在 ChannelPipeline 中创建一组自定义的 ChannelHandler 对象。

ChannelInitializer 将它自己从 ChannelPipeline 中移除。

ChannelInitializer 是一个特殊的 ChannelInboundHandlerAdapter 抽象类。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354