【Netty】Netty组件介绍
# Netty 组件介绍
Netty 有 Bootstrap/ServerBootstrap,Channel,EventLoop,ChannelFuture, ChannelHandler,ChannelPipeline,编码器和解码器等核心组件。
# Bootstrap/ServerBootstrap
Bootstrap 和 ServerBootstrap 是 Netty 应用程序的引导类,它提供了用于应用程序网络层的配置。 一般的 Netty 应用程序总是分为客户端和服务端,所以引导分为客户端引导 Bootstrap 和服务端引导 ServerBootstrap, ServerBootstrap 作为服务端引导,它将服务端进程绑定到指定的端口,而 Bootstrap 则是将客户端连接到 指定的远程服务器。 Bootstrap 和 ServerBootstrap 除了职责不同,它们所需的 EventLoopGroup 的数量也不同, Bootstrap 引导客户端只需要一个 EventLoopGroup,而 ServerBootstrap 则需要两个 EventLoopGroup。
# Channel
在我们使用某种语言,如 c/c++,java,go 等,进行网络编程的时候,我们通常会使用到 Socket, Socket 是对底层操作系统网络 IO 操作(如 read,write,bind,connect 等)的封装, 因此我们必须去学习 Socket 才能完成网络编程,而 Socket 的操作其实是比较复杂的,想要使用好它有一定难度, 所以 Netty 提供了 Channel(io.netty.Channel,而非 java nio 的 Channel),更加方便我们处理 IO 事件。
# EventLoop
EventLoop 用于服务端与客户端连接的生命周期中所发生的事件。 EventLoop 与 EventLoopGroup,Channel 的关系模型如下:
一个 EventLoopGroup 通常包含一个或多个 EventLoop,一个 EventLoop 可以处理多个 Channel 的 IO 事件, 一个 Channel 也只会被注册到一个 EventLoop 上。在 EventLoop 的生命周期中,它只会和一个 Thread 线程绑定,这个 EventLoop 处理的 IO 事件都将在与它绑定的 Thread 内被处理。
# ChannelFuture
在 Netty 中,所有的 IO 操作都是异步执行的,所以一个操作会立刻返回,但是如何获取操作执行完的结果呢? Netty 就提供了 ChannelFuture 接口,它的 addListener 方法会向 Channel 注册 ChannelFutureListener, 以便在某个操作完成时得到通知结果。
# ChannelHandler
我们知道 Netty 是一个款基于事件驱动的网络框架,当特定事件触发时,我们能够按照自定义的逻辑去处理数据。 ChannelHandler 则正是用于处理入站和出站数据钩子,它可以处理几乎所有类型的动作,所以 ChannelHandler 会是 我们开发者更为关注的一个接口。
ChannelHandler 主要分为处理入站数据的 ChannelInboundHandler 和出站数据的 ChannelOutboundHandler 接口。
Netty 以适配器的形式提供了大量默认的 ChannelHandler 实现,主要目的是为了简化程序开发的过程,我们只需要 重写我们关注的事件和方法就可以了。 通常我们会以继承的方式使用以下适配器和抽象:
- ChannelHandlerAdapter
- ChannelInboundHandlerAdapter
- ChannelDuplexHandler
- ChannelOutboundHandlerAdapter
# ChannelPipeline
上面介绍了 ChannelHandler 的作用,它使我们更关注于特定事件的数据处理,但如何使我们自定义的 ChannelHandler 能够在事件触发时被使用呢? Netty 提供了 ChannelPipeline 接口,它 提供了存放 ChannelHandler 链的容器,且 ChannelPipeline 定义了在这条 ChannelHandler 链上 管理入站和出站事件流的 API。 当一个 Channel 被初始化时,会使用 ChannelInitializer 接口的 initChannel 方法在 ChannelPipeline 中 添加一组自定义的 ChannelHandler。
# 入站事件和出站事件的流向
从服务端角度来看,如果一个事件的运动方向是从客户端到服务端,那么这个事件是入站的,如果事件运动的方向 是从服务端到客户端,那么这个事件是出站的。
上图是 Netty 事件入站和出站的大致流向,入站和出站的 ChannelHandler 可以被安装到一个 ChannelPipeline 中, 如果一个消息或其他的入站事件被[读取],那么它会从 ChannelPipeline 的头部开始流动,并传递给第一个 ChannelInboundHandler ,这个 ChannelHandler 的行为取决于它的具体功能,不一定会修改消息。 在经历过第一个 ChannelInboundHandler 之后, 消息会被传递给这条 ChannelHandler 链的下一个 ChannelHandler,最终消息会到达 ChannelPipeline 尾端,消息的读取也就结束了。
数据的出站(消息被[写出])流程与入站是相似的,在出站过程中,消息从 ChannelOutboundHandler 链的尾端开始流动, 直到到达它的头部为止,在这之后,消息会到达网络传输层进行后续传输。
# 进一步了解 ChannelHandler
鉴于入站操作和出站操作是不同的,可能有同学会疑惑:为什么入站 ChannelHandler 和出站 ChannelHandler 的数据 不会窜流呢(为什么入站的数据不会到出站 ChannelHandler 链中)? 因为 Netty 可以区分 ChannelInboundHandler 和 ChannelOutboundHandler 的实现,并确保数据只在两个相同类型的 ChannelHandler 直接传递,即数据要么在 ChannelInboundHandler 链之间流动,要么在 ChannelOutboundHandler 链之间流动。
当 ChannelHandler 被添加到 ChannelPipeline 中后,它会被分配一个 ChannelHandlerContext, 它代表了 ChannelHandler 和 ChannelPipeline 之间的绑定。 我们可以使用 ChannelHandlerContext 获取底层的 Channel,但它最主要的作用还是用于写出数据。
# 编码器和解码器
当我们通过 Netty 发送(出站)或接收(入站)一个消息时,就会发生一次数据的转换,因为数据在网络中总是通过字节传输的, 所以当数据入站时,Netty 会解码数据,即把数据从字节转为为另一种格式(通常是一个 Java 对象), 当数据出站时,Netty 会编码数据,即把数据从它当前格式转为为字节。
Netty 为编码器和解码器提供了不同类型的抽象,这些编码器和解码器其实都是 ChannelHandler 的实现, 它们的名称通常是 ByteToMessageDecoder 和 MessageToByteEncoder。
对于入站数据来说,解码其实是解码器通过重写 ChannelHandler 的 read 事件,然后调用它们自己的 decode 方法完成的。 对于出站数据来说,编码则是编码器通过重写 ChannelHandler 的 write 事件,然后调用它们自己的 encode 方法完成的。
为什么编码器和解码器被设计为 ChannelHandler 的实现呢?
我觉得这很符合 Netty 的设计,上面已经介绍过 Netty 是一个事件驱动的框架,其事件由特定的 ChannelHandler 完成,我们从用户的角度看,编码和解码其实是属于应用逻辑的,按照应用逻辑实现自定义的编码器和解码器就是 理所应当的。
# SimpleChannelInboundHandler
在我们编写 Netty 应用程序时,会使用某个 ChannelHandler 来接受入站消息,非常简单的一种方式 是扩展 SimpleChannelInboundHandler< T >,T 是我们需要处理消息的类型。 继承 SimpleChannelInboundHandler 后,我们只需要重写其中一个或多个方法就可以完成我们的逻辑。
- 01
- idea 热部署插件 JRebel 安装及破解,不生效问题解决04-10
- 02
- spark中代码的执行位置(Driver or Executer)12-12
- 03
- 大数据技术之 SparkStreaming12-12