计算机系统应用教程网站

网站首页 > 技术文章 正文

Netty基础知识整理

btikc 2024-09-04 03:25:19 技术文章 9 ℃ 0 评论

Netty的定义

Netty 是一个高性能、异步事件驱动的 NIO 框架,用于快速开发可维护的高性能协议服务器和客户端,基于 JAVA NIO 提供的 API 实现。它提供了对TCP、UDP 和文件传输的支持,作为一个异步 NIO 框架,Netty 的所有 IO 操作都是异步非阻塞的,通过 Future-Listener 机制,用户可以方便的主动获取或者通过通知机制获得 IO 操作结果。

为什么使用Netty

  1. 高性能:吞吐量更高,延迟更低,资源消耗减少,最小化不必要的内存复制,与目前多种NIO主流框架相比,Netty综合性能最高。
  2. 高稳定性:Netty修复了已经发现的所有JDK NIO中的BUG。比如epoll bug,这个BUG会在linux上导致cpu 100%,使得nio server/client不可用,这个BUG直到jdk 6u4才解决,但是直到JDK1.7中仍然有这个问题,该问题并未被完全解决,只是发生的频率降低了而已。
  3. 定制能力高:可以通过ChannelHandler对通信框架进行灵活地拓展。
  4. 功能强大:预置了多种编解码功能,支持多种主流协议。
  5. 设计优雅:统一的API,适用于不同的协议(阻塞和非阻塞);基于灵活、可扩展的事件驱动模型;高度可定制的线程模型;可靠的无连接数据Socket支持(UDP)。
  6. 经历了大规模的商业应用考验,质量和可靠性都有很好的验证。

Netty能够做什么

  1. 作为基础通信组件被这些 RPC 框架使用,Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信,Dubbo 协议默认使用 Netty 作为基础通信组件,用于实现各进程节点之间的内部通信。
  2. 大数据计算往往采用多个计算节点和一个/N个汇总节点进行分布式部署,各节点之间存在海量的数据交换。由于 Netty 的综合性能是目前各个成熟 NIO 框架中最高的,因此,往往会被选中用作大数据各节点间的通信。

Netty整体结构


Netty由核心(Core)、传输服务(Transport Servies)以及协议支持(Protocol Support)这几个模块组成。核心模块提供了性能极高的零拷贝能力,还提供了统一的通信API和可高度扩展的事件驱动模型。传输服务模块和协议支持模块是对 Netty 的有力补充。传输服务模块支持了TCP和UDP等Socket通信,以及HTTP和同一JVM内的通信通道。协议支持模块则对常见的序列化协议进行支持,如Protobuf、gzip等。

Netty组件


Channel

Channel 是 Java NIO 的一个基本构造。 它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作的程序组件)的开放连接,如读操作和写操作。 目前,可以把 Channel 看作是传入(入站)或者传出(出站)数据的载体。因此,它可以被打开或者被关闭,连接或者断开连接。

回调

一个回调其实就是一个方法,一个指向已经被提供给另外一个方法的方法的引用。Netty 在内部使用了回调来处理事件;当一个回调被触发时,相关的事件可以被一个ChannelHandler 的实现处理。例如当一个新的连接已经被建立时, ChannelHandler 的 channelActive()回调方法将会被调用。

ChannelFuture

Future 提供了另一种在操作完成时通知应用程序的方式。这个对象可以看作是一个异步操作的结果的占位符; 它将在未来的某个时刻完成,并提供对其结果的访问。Netty的ChannelFuture,用于在执行异步操作的时候使用。

ChannelFuture接口的addListener()方法注册了一个ChannelFutureListener,以便在某个操作完成时(无论是否成功)得到通知。监听器的回调方法operationComplete(),将会在对应的操作完成时被调用 。然后监听器可以判断该操作是成功地完成了还是出错了。每个 Netty 的出站 I/O 操作都将返回一个 ChannelFuture,不会阻塞,所以Netty完全是异步和事件驱动的。

可以把ChannelFutureListener 看作是回调的一个更加精细的版本,回调和 Future 是相互补充的机制;它们相互结合,构成了 Netty 本身的关键构件块之一。

Netty 的异步编程模型是建立在 Future 和回调的概念之上的。

EventLoop

控制流、多线程处理、并发;

EventLoop 定义了 Netty 的核心抽象,用于处理连接的生命周期中所发生的事件。

Netty 通过触发事件将 Selector 从应用程序中抽象出来,消除了所有本来将需要手动编写的派发代码。在内部,将会为每个Channel分配一个 EventLoop,用以处理所有事件,包括: 注册感兴趣的事件,将事件派发给 ChannelHandler,以及安排进一步的动作。
EventLoop 本身只由一个线程驱动,其处理了一个 Channel 的所有 I/O 事件,并且在该EventLoop 的整个生命周期内都不会改变。这个简单而强大的设计消除了你可能有的在 ChannelHandler 实现中需要进行同步的任何顾虑,因此,你可以专注于提供正确的逻辑,用来在有感兴趣的数据要处理的时候执行。

EventLoopGroup

一个 EventLoopGroup 包含一个或者多个 EventLoop;
一个 EventLoop 在它的生命周期内只和一个 Thread 绑定;
所有由 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理;
一个 Channel 在它的生命周期内只注册于一个 EventLoop;
一个 EventLoop 可能会被分配给一个或多个 Channel。 在这种设计中,一个给定 Channel 的 I/O 操作都是由相同的 Thread 执行的,实际上消除了对于同步的需要。


事件和ChannelHandler

Netty 使用不同的事件来通知我们状态的改变或者是操作的状态。这使得我们能够基于已经 发生的事件来触发适当的动作。这些动作可能是: 记录日志、数据转换、流控制或者应用程序逻辑。Netty 是一个网络编程框架,所以事件是按照它们与入站或出站数据流的相关性进行分类的。

可能由入站数据或者相关的状态更改而触发的事件包括: 连接已被激活或者连接失活,异步地连接到远程节点,数据读取,用户事件或者错误事件。

出站事件是未来将会触发的某个动作的操作结果,这些动作包括: 打开或者关闭到远程节点的连接,将数据写到或者冲刷到套接字。

Netty 的主要组件是 ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器。事实上,ChannelHandler 可专门用于几乎任何类型的动作,例如将数据从一种格式转换为另外一种格式,或者处理转换过程 中所抛出的异常。

使得事件流经 ChannelPipeline 是 ChannelHandler 的工作,它们是在应用程序的初 始化或者引导阶段被安装的。这些对象接收事件、执行它们所实现的处理逻辑,并将数据传递给链中的下一个 ChannelHandler。它们的执行顺序是由它们被添加的顺序所决定的。

ChannelPipeline

ChannelPipeline 提供了 ChannelHandler 链的容器,并定义了用于在该链上传播入站 和出站事件流的 API。当 Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。

ChannelHandler 安装到 ChannelPipeline 中的过程如下所示:

  1. 一个ChannelInitializer的实现被注册到了ServerBootstrap中;
  2. 当 ChannelInitializer.initChannel()方法被调用时,ChannelInitializer将在 ChannelPipeline 中安装一组自定义的ChannelHandler;
  3. ChannelInitializer 将它自己从 ChannelPipeline 中移除。

如果一个消息或者任何其他的入站事件被读取,那么它会从 ChannelPipeline 的头部开始流动,并被传递给第一个 ChannelInboundHandler。这个 ChannelHandler不一定会实际地修改数据,具体取决于它的具体功能,在这之后,数据将会被传递给链中的下一个ChannelInboundHandler。最终,数据将会到达ChannelPipeline的尾端,届时,所有处理就都结束了。

数据的出站运动(即正在被写的数据)在概念上也是一样的。在这种情况下,数据将从ChannelOutboundHandler链的尾端开始流动,直到它到达链的头部为止。在这之后,出站数据将会到达网络传输层,这里显示为 Socket。通常情况下,这将触发一个写操作。

当 ChannelHandler 被添加到 ChannelPipeline 时,它将会被分配一个 ChannelHandlerContext,其代表了 ChannelHandler 和 ChannelPipeline 之间的绑定。虽然这个对象可以被用于获取底层的 Channel,但是它主要还是被用于写出站数据。

ChannelPipeline 中的每个ChannelHandler将负责把事件转发到链中的下一个ChannelHandler。通过使用作为参数传递到每个方法的ChannelHandlerContext,事件可以被传递给当前ChannelHandler 链中的下一个 ChannelHandler。

在Netty中,有两种发送消息的方式。你可以直接写到Channel中,也可以 写到和ChannelHandler 相关联的 ChannelHandlerContext 对象中。前一种方式将会导致消息从 ChannelPipeline 的尾端开始流动,而后者将导致消息从 ChannelPipeline 中的下一个 ChannelHandler 开始流动。

编码器和解码器

Netty提供的编码器/解码器适配器类都实现了ChannelOutboundHandler或者ChannelInboundHandler 接口。对于入站数据来说,channelRead 方法/事件已经被重写了。对于每个从入站 Channel 读取的消息,这个方法都将会被调用。随后,它将调用由预置解码器所提供的 decode() 方法,并将已解码的字节转发给 ChannelPipeline 中的下一个 ChannelInboundHandler。

出站消息的模式是相反方向的:编码器将消息转换为字节,并将它们转发给下一个 ChannelOutboundHandler。

BootStrap

Netty 的引导类为应用程序的网络层配置提供了容器,这涉及将一个进程绑定到某个指定的端口,或者将一个进程连接到另一个运行在某个指定主机的指定端口上的进程。

ServerBootstrap 将绑定到一个 端口,因为服务器必须要监听连接,而 Bootstrap 则是由想要连接到远程节点的客户端应用程序所使用的。

服务器需要两组不同的 Channel。第一组将只包含一个 ServerChannel,代表服务 器自身的已绑定到某个本地端口的正在监听的套接字。而第二组将包含所有已创建的用来处理传 入客户端连接(对于每个服务器已经接受的连接都有一个)的Channel。

ByteBuf

Netty ByteBuff的优点:

可以被用户自定义的缓冲区类型扩展;

通过内置的复合缓冲区类型实现了透明的零拷贝;

容量可以按需增长(类似于 JDK 的 StringBuilder);

在读和写这两种模式之间切换不需要调用 ByteBuffer 的 flip()方法;

读和写使用了不同的索引;

支持方法的链式调用;

支持引用计数;

支持池化。

Netty服务端创建流程


IO模型对比



阻塞IO

在用户进程发起 I/O 操作后,需要等待操作完成才能继续运行。

非阻塞IO

在用户进程发起I/O操作后,无须等待操作完成即可继续进行其他操作,但用户进程需要定期询问I/O操作是否就绪。可以使用一个线程监听所有的 Socket请求,从而极大地减少线程数量。

判断阻塞I/O与非阻塞I/O时应关注程序是否在等待调用结果——如果系统内核中的数据还未准备完成,用户进程是继续等待直至准备完成,还是直接返回并先处理其他事情。

同步IO

在系统内核准备好处理数据后,还需要等待内核将数据复制到用户进程,才能进行处理。

异步IO

用户进程无须关心实际I/O的操作过程,只需在I/O完成后由内核接收通知,I/O操作全部由内核进程来执行。

判断是同步I/O还是异步I/O,主要关注内核数据复制到用户空间时是否需要等待。

BIO同步阻塞IO

BIO 服务器实现模式为每一个连接都分配了一个线程,即客户端有连接请求时,服务端就需要启动一个线程进行处理。它缺乏弹性伸缩能力,服务端的线程个数和客户端并发访问数呈正比,随着访问量的增加,线程数量会迅速膨胀,最终导致系统性能急剧下降。可以通过合理使用线程池来改进“一连接一线程”模型,实现一个线程处理多个客户端,但开启线程的数量终归会受到系统资源的限制,而且频繁进行线程上下文切换也会导致CPU的利用率降低。

NIO同步非阻塞IO

NIO通过事件模型的异步通知机制去处理输入/输出的相关操作。在客户端的连接建立完毕且读取准备就绪后,位于服务端的连接接收器便会触发相关事件。与BIO不同,NIO的一切处理都是通过事件驱动的,客户端连接到服务端并创建通信管道,服务端会将通信管道注册到事件选择器,由事件选择器接管事件的监听,并派发至工作线程进行读取、编解码、计算以及发送。

一个Selector可以同时注册、监听和轮询成百上千个Channel,一个用于处理I/O的线程可以同时并发处理多个客户端连接,具体数量取决于进程可用的最大文件句柄数。由于处理 I/O的线程数大幅减少,因此CPU用于处理线程切换和竞争的时间也相应减少,即NIO中的CPU利用率比BIO中的CPU利用率大幅提高

参考衔接

  1. https://blog.csdn.net/xu_melon/article/details/79201198
  2. https://blog.csdn.net/qq_35571554/article/details/82786257
  3. https://www.cnblogs.com/chenxiaoxian/p/10431765.html
  4. https://segmentfault.com/a/1190000017128263?utm_source=tag-newest
  5. Netty实战书籍
  6. 未来架构:从服务化到云原生

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表