本文来说下Netty 核心组件

文章目录

  • 核心组件概述
  • Bytebuf字节容器
  • Bootstrap和ServerBootstrap(启动引导类)
  • Channel(网络操作抽象类)
  • EventLoop(事件循环)
  • ChannelHandler(消息处理器)和 ChannelPipeline(ChannelHandler 对象链表)
    • ChannelHandler
    • ChannelPipeline
    • 入站事件和出站事件的流向
    • ChannelFuture(操作执行结果)
  • 本文小结

核心组件概述

下面枚举所有的 Netty 应用程序的基本构建模块(核心组件),包括客户端和服务器:

  • Bytebuf(字节容器)
  • Bootstrap 和 ServerBootstrap (启动引导类)
  • Channel(网络操作抽象类)
  • EventLoop (事件循环)
  • ChannelHandler (消息处理器)和 ChannelPipeline (ChannelHandler 对象链表)
  • ChannelFuture(操作执行结果)

通过下面这张图你可以将我提到的这些 Netty 核心组件串联起来:


Bytebuf字节容器

网络通信最终都是通过字节流进行传输的。 Netty 使用自建的 buffer API,而不是使用 NIO 的 ByteBuffer 来存储连续的字节序列。与 ByteBuffer 相比这种方式拥有明显的优势。Netty 使用新的 buffer 类型 ByteBuf,被设计为一个可从底层解决 ByteBuffer 问题,并可满足日常网络应用开发需要的缓冲类型。这些很酷的特性包括:

  • 如果需要,允许使用自定义的缓冲类型。
  • 复合缓冲类型中内置的透明的零拷贝实现。
  • 开箱即用的动态缓冲类型,具有像 StringBuffer 一样的动态缓冲能力。
  • 不再需要调用的 flip() 方法。
  • 正常情况下具有比 ByteBuffer 更快的响应速度。

Bootstrap和ServerBootstrap(启动引导类)

Bootstrap 是客户端的启动引导类/辅助类,不管程序使用哪种协议,无论是创建一个客户端还是服务器都需要使用“引导”。具体使用方法如下:

 EventLoopGroup group = new NioEventLoopGroup();try {//创建客户端启动引导/辅助类:BootstrapBootstrap b = new Bootstrap();//指定线程模型b.group(group).......// 尝试建立连接ChannelFuture f = b.connect(host, port).sync();f.channel().closeFuture().sync();} finally {// 优雅关闭相关线程组资源group.shutdownGracefully();}

ServerBootstrap 客户端的启动引导类/辅助类,具体使用方法如下:

    // 1.bossGroup 用于接收连接,workerGroup 用于具体的处理EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {// 2.创建服务端启动引导/辅助类:ServerBootstrapServerBootstrap b = new ServerBootstrap();// 3.给引导类配置两大线程组,确定了线程模型b.group(bossGroup, workerGroup).......// 6.绑定端口ChannelFuture f = b.bind(port).sync();// 等待连接关闭f.channel().closeFuture().sync();} finally {// 7.优雅关闭相关线程组资源bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}

从上面的示例中,我们可以看出:

  • Bootstrap 通常使用 connet() 方法连接到远程的主机和端口,作为一个 Netty TCP 协议通信中的客户端。另外,Bootstrap 也可以通过 bind() 方法绑定本地的一个端口,作为 UDP 协议通信中的一端。
  • ServerBootstrap通常使用 bind() 方法绑定本地的端口上,然后等待客户端的连接。
  • Bootstrap 只需要配置一个线程组 EventLoopGroup , 而 ServerBootstrap 需要配置两个线程组— EventLoopGroup ,一个用于接收连接,一个用于具体的 IO 处理。

Channel(网络操作抽象类)

在我们使用某种语言,如 c/c++,java,go 等,进行网络编程的时候,我们通常会使用到 Socket, Socket 是对底层操作系统网络 IO 操作(如 read,write,bind,connect等)的封装, 因此我们必须去学习 Socket 才能完成网络编程,而 Socket 的操作其实是比较复杂的,想要使用好它有一定难度, 所以 Netty 提供了Channel(注意是 io.netty.Channel,而非 Java NIO 的 Channel),更加方便我们处理 IO 事件。

Channel 接口是 Netty 对网络操作抽象类。通过 Channel 我们可以进行 I/O 操作。Channel 为用户提供:

  • 当前网络连接的通道的状态(例如是否打开?是否已连接?)
  • 网络连接的配置参数 (例如接收缓冲区大小)
  • 提供异步的网络 I/O 操作 (如建立连接,读写,绑定端口),异步调用意味着任何 I/O调用都将立即返回,并且不保证在调用结束时所请求的 I/O 操作已完成。调用后立即返回一个 ChannelFuture 实例,通过注册监听器到ChannelFuture 上,可以在 I/O操作成功、失败或取消时回调通知调用方。
  • 支持关联 I/O 操作与对应的处理程序

一旦客户端成功连接服务端,就会新建一个 Channel 同该用户端进行绑定,示例代码如下:

// 通过 Bootstrap 的 connect 方法连接到服务端public Channel doConnect(InetSocketAddress inetSocketAddress) {CompletableFuture<Channel> completableFuture = new CompletableFuture<>();bootstrap.connect(inetSocketAddress).addListener((ChannelFutureListener) future -> {if (future.isSuccess()) {completableFuture.complete(future.channel());} else {throw new IllegalStateException();}});return completableFuture.get();}

比较常用的Channel接口实现类是 :

  • NioServerSocketChannel(服务端)
  • NioSocketChannel(客户端)

这两个 Channel 可以和 BIO 编程模型中的 ServerSocket以及Socket两个概念对应上。


EventLoop(事件循环)

① EventLoop 概述

EventLoop(事件循环)接口可以说是 Netty 中最核心的概念了!EventLoop 定义了 Netty 的核心抽象,用于处理连接的生命周期中所发生的事件。

是不是很难理解?说白了,EventLoop 的主要作用实际就是责监听网络事件并调用事件处理器进行相关 I/O 操作(读写)的处理。

② Channel 和 EventLoop 的关系

那 Channel 和 EventLoop 直接有啥联系呢?

Channel 为 Netty 网络操作(读写等操作)抽象类,EventLoop 负责处理注册到其上的Channel 的 I/O 操作,两者配合进行 I/O 操作。

③ EventloopGroup 和 EventLoop 的关系

EventLoopGroup 包含多个 EventLoop(每一个 EventLoop 通常内部包含一个线程),它管理着所有的 EventLoop 的生命周期。

并且,EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理,即 Thread 和 EventLoop 属于 1 : 1 的关系,从而保证线程安全。

下图是 Netty NIO 模型对应的 EventLoop 模型。通过这个图应该可以将 EventloopGroup、EventLoop、 Channel 三者联系起来:


ChannelHandler(消息处理器)和 ChannelPipeline(ChannelHandler 对象链表)

ChannelHandler

我们知道 Netty 是一个款基于事件驱动的网络框架,当特定事件触发时,我们能够按照自定义的逻辑去处理数据。 ChannelHandler 则正是用于处理入站(接收)和出站(发送)数据钩子,它可以处理几乎所有类型的动作,所以 ChannelHandler 会是 我们开发者更为关注的一个接口。

通俗来说,ChannelHandler 是消息的具体处理器,主要负责处理客户端/服务端接收和发送的数据

ChannelHandler 主要分为处理入站数据的 ChannelInboundHandler 和出站数据的 ChannelOutboundHandler 接口。

Netty 以适配器的形式提供了大量默认的 ChannelHandler 实现,主要目的是为了简化程序开发的过程,我们只需要 重写我们关注的事件和方法就可以了。 通常我们会以继承的方式使用以下适配器和抽象:

  • ChannelHandlerAdapter
  • ChannelInboundHandlerAdapter
  • ChannelDuplexHandler
  • ChannelOutboundHandlerAdapter

ChannelPipeline

当 Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。 一个 Channel 包含一个 ChannelPipeline。 ChannelPipeline 为 ChannelHandler 的链,一个 pipeline 上可以有多个 ChannelHandler。

我们可以在 ChannelPipeline 上通过 addLast() 方法添加一个或者多个ChannelHandler (一个数据或者事件可能会被多个 Handler 处理) 。当一个 ChannelHandler 处理完之后就将数据交给下一个 ChannelHandler :

 b.group(eventLoopGroup).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new NettyKryoDecoder(kryoSerializer, RpcResponse.class));ch.pipeline().addLast(new NettyKryoEncoder(kryoSerializer, RpcRequest.class));ch.pipeline().addLast(new KryoClientHandler());}});

入站事件和出站事件的流向

从服务端角度来看,如果一个事件的运动方向是从客户端到服务端,那么这个事件是入站的,如果事件运动的方向 是从服务端到客户端,那么这个事件是出站的。


上图是 Netty 事件入站和出站的大致流向,入站和出站的 ChannelHandler 可以被安装到一个ChannelPipeline中, 如果一个消息或其他的入站事件被[读取],那么它会从ChannelPipeline的头部开始流动,并传递给第一个ChannelInboundHandler ,这个ChannelHandler的行为取决于它的具体功能,不一定会修改消息。 在经历过第一个ChannelInboundHandler之后, 消息会被传递给这条ChannelHandler链的下一个ChannelHandler,最终消息会到达ChannelPipeline尾端,消息的读取也就结束了。

数据的出站 (发送) 流程与入站是相似的,在出站过程中,消息从ChannelOutboundHandler链的尾端开始流动, 直到到达它的头部为止,在这之后,消息会到达网络传输层进行后续传输。

鉴于入站操作和出站操作是不同的,可能有同学会疑惑:❓ 为什么入站 ChannelHandler和出站 ChannelHandler的数据 不会窜流呢(为什么 入站 的数据不会到出站 ChannelHandler 链中)?

因为Netty可以区分ChannelInboundHandler和 ChannelOutboundHandler的实现,并确保数据只在两个相同类型的ChannelHandler直接传递,即数据要么在 ChannelInboundHandler链之间流动,要么在ChannelOutboundHandler链之间流动。

当ChannelHandler被添加到ChannelPipeline中后,它会被分配一个ChannelHandlerContext, 它代表了ChannelHandler和ChannelPipeline之间的绑定。 ChannelPipeline 通过 ChannelHandlerContext来间接管理 ChannelHandler 。



ChannelFuture(操作执行结果)

public interface ChannelFuture extends Future<Void> {Channel channel();ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> var1);......ChannelFuture sync() throws InterruptedException;
}

Netty 是异步非阻塞的,所有的 I/O 操作都为异步的。因此,我们不能立刻得到操作是否执行成功,但是,你可以通过 ChannelFuture 接口的 addListener() 方法注册一个监听 ChannelFutureListener,当操作执行成功或者失败时,监听就会自动触发返回结果。

ChannelFuture f = b.connect(host, port).addListener(future -> {if (future.isSuccess()) {System.out.println("连接成功!");} else {System.err.println("连接失败!");}
}).sync();

并且,你还可以通过ChannelFuture 的 channel() 方法获取连接相关联的Channel 。

Channel channel = f.channel();

另外,我们还可以通过 ChannelFuture 接口的 sync()方法让异步的操作编程同步的。

// bind()是异步的,但是,你可以通过 `sync()`方法将其变为同步。
ChannelFuture f = b.bind(port).sync();

本文小结

本文详细介绍了netty的核心组件以及知识,对netty的理解与掌握有极大的帮助。

Netty核心组件总览相关推荐

  1. 【Netty】Netty 核心组件 ( ChannelHandlerContext )

    文章目录 一. ChannelHandlerContext 组件 二. ChannelHandlerContext 组件 debug 调试 三. ChannelHandlerContext 组件 de ...

  2. 【Netty】Netty 核心组件 ( ChannelPipeline 中的 ChannelHandlerContext 双向链表分析 )

    文章目录 一. 代码示例分析 二. ChannelHandlerContext 双向链表类型 三. Pipeline / ChannelPipeline 管道内双向链表分析 四. 数据入站与出站 接上 ...

  3. 五、Netty核心组件

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

  4. 【Netty】大白话 Netty 核心组件分析

    Bytebuf(字节容器) 网络通信最终都是通过字节流进行传输的. ByteBuf 就是 Netty 提供的一个字节容器,其内部是一个字节数组. 当我们通过 Netty 传输数据的时候,就是通过 By ...

  5. 【Netty】Netty 核心组件 ( Pipeline | ChannelPipeline )

    文章目录 一. Pipeline / ChannelPipeline 管道组件 二. Pipeline / ChannelPipeline 管道组件元素解析 一. Pipeline / Channel ...

  6. 【Netty】Netty 核心组件 ( Future | Channel | Selector | ChannelHandler )

    文章目录 一. Future / ChannelFuture 异步操作监听组件 二. Channel 通道组件 三. Selector 选择器组件 四. ChannelHandler 通道处理器组件 ...

  7. 【Netty】Netty 核心组件 ( ServerBootstrap | Bootstrap )

    文章目录 一. ServerBootstrap / Bootstrap 引导组件 二. 服务器端 ServerBootstrap 引导组件 三. 客户端 Bootstrap 引导组件 一. Serve ...

  8. Netty核心组件 ChannelPipeline和ChannelHandler与ChannelHandler的入站出站规则

    概述 Netty中ChannelPipeline与Channel的对应关系是一一对应,也就是每个Channel中有且仅有一个ChannelPipeline,可以通过Channel获取唯一的Channe ...

  9. Netty 核心组件源码剖析

    文章目录 1.EventLoopGroup 1.1 NioEventLoopGroup 1.2 NioEventLoop 1.2.1 NioEventLoop 开启 Selector 1.2.2 Ni ...

最新文章

  1. matlab 图像中用text文字标注
  2. 生成allure测试报告时报错的解决方法
  3. 124 Binary Tree Maximum Path Sum
  4. [UE4]C++中extern关键字浅谈
  5. java实现人脸识别源码【含测试效果图】——ServiceImpl层(UserServiceImpl)
  6. 前端学习(2357):uni的基本数据绑定
  7. Linux相关配置 集群免密码登录配置
  8. Apache PDFbox快速开发指南
  9. 服务器系统分区 是啥,服务器系统盘分区
  10. 如何使用Keras功能API进行深度学习
  11. C语言入门知识集合。
  12. 知其然也知其所以然,Redis笔记总结:核心原理与应用实践
  13. 一个springboot 项目a集成另一个springboot 项目b
  14. WebRTC会成主流吗?P2P流媒体时代到了!
  15. 云呐|仓库RFID固定资产盘点软件有哪些优势
  16. unity实现war3真三国无双(一)资源提取
  17. egg使用egg-socket.io
  18. 多元多项式除法python_python中多项式的除法
  19. 对实体完整性和参照完整性的认识
  20. java 天数转毫秒_Java 毫秒转换为日期类型、日期转换为毫秒

热门文章

  1. Datrium公司以几近疯狂的方式提升速度表现
  2. 精简Linux文件路径
  3. 【noi】植物大战僵尸
  4. ceph升级到10.2.3 版本启动服务报错:Unknown lvalue 'TasksMax' in section 'Service'
  5. Atitit  从 RGB 到 HSL 或 HSV 的转换
  6. Java基础-SSM之mybatis快速入门篇
  7. 第 37 章 ACOS - CLI
  8. 实践 Redux,第 1 部分: Redux-ORM 基础
  9. Spark Streaming中流式计算的困境与解决之道
  10. 带命名空间的XML的dom4j应用转