「Netty入门」一分钟了解Netty

发布时间 2023-07-01 00:05:47作者: 二进制狂人

前言

此篇是真正进入Netty学习的第一篇,学好Netty之前,要先了解系统中断、IO多路复用、NIO这些必不可少的基础知识,否则学起Netty就比较吃力,所以如果小伙伴还没有看我前面几篇基础知识的可以先学习完再回过头看这篇文章。

[硬核Netty系列]什么是操作系统中断?
[硬核Netty系列]IO多路复用底层原理详解,Java面试大厂必问
Java NIO 想彻底了解?最走心的NIO讲解带给你

概念

Netty是基于Java NIO的异步事件驱动的网络应用框架。它封装了网络编程的复杂性,使用者无需关心如何进行复杂的网络调用,只需通过netty即可完成,从而让开发更简单便捷。
Netty是目前最流行的NIO框架,许多框架的底层rpc都是用的Netty,例如Dubbo、Elasticearch。

为什么Netty那么受欢迎

Netty之所以那么受欢迎,我觉得主要因为Netty有以下几个优点

  • 统一的API,支持多种传输类型,阻塞和非阻塞。
  • 简单而强大的线程模型
  • 真正的无连接数据报套接字(UDP)支持
  • 链接逻辑组件以支持复用
  • 拥有比Java的核心API更高的吞吐量以及更低的延迟
  • 拥有更低的资源消耗,最少的内存复制
  • 不会因为慢速、快速或者超载的连接而导致OutOfMemoryError

Netty核心组件

Netty的核心组件大致可以分为Channel、回调、Future、事件和ChannelHandler。
这些构建代表了不同类型的构造:资源、逻辑以及通知。

Channel

它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作的程序组件)的开放连接,如读操作和写操作。
以下是常用的Channel:

  • EmbeddedChannel
  • LocalServerChannel
  • NioDatagramChannel
  • NioSctpChannel
    相对于原生NIO的Channel,Netty的Channel具有如下优势
  • 在 Channel 接口层,采用 Facade 模式进行统一封装,将网络 I/O 操作、网络 I/O 相关联的其他操作封装起来,统一对外提供。
  • Channel 接口的定义尽量大而全,为 SocketChannel 和 ServerSocketChannel 提供统一的视图,由不同子类实现不同的功能,公共功能在抽象父类中实现,最大程度地实现功能和接口的重用。
  • 具体实现采用聚合而非包含的方式,将相关的功能类聚合在 Channel 中,有 Channel 统一负责和调度,功能实现更加灵活。

回调

回调其实就是一个方法,指向已经被提供给另外一个方法的方法的引用。这也是在操作完成后通知相关方最常见的方式之一。

在Netty中使用了回调来处理事件,当一个回调被触发时,相关的事件可以被ChannelHandler的实现来处理。当一个新的连接已经被建立时,ChannelHandler的channelActive()回调方法将会被调用。

ChannelHandler

Netty的主要组件就是ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器。

Netty 使用不同的事件来通知我们状态的改变或者是操作的状态,每个事件都可以被分发给ChannelHandler类中某个用户实现的方法。Netty提供了大量预定义的可以开箱即用的ChannelHandler实现,包括用于各种协议的ChannelHandler。
如果ChannelHandler处理完成后不直接返回给客户端,而是传递给下一个ChannelHandler继续处理,就要用到ChannelPipeline

ChannelPipeline

ChannelPipeline提供了ChannelHandler链的容器,并定义了用在该链上传播入站和出站事件流的API。
ChannelPipeline 提供了 ChannelHandler链 的容器,并定义了用于在该链上传播入站和出站事件流的API。使得事件流经 ChannelPipeline 是 ChannelHandler 的工作,它们是在应用程序的初始化或者引导阶段被安装的。这些对象接收事件、执行他们所实现的处理逻辑,并将数据传递给链中的下一个ChannelHandler:

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

 

ChannelFuture

Future提供了另一种在操作完成时通知应用程序的方式。它将在未来某个时刻完成,并提供对其结果的访问。

在jdk并发包中提供的Future只允许手动检查对应的操作是否已经完成,或者一直阻塞直到它完成。这非常繁琐,Netty提供了它自己的实现ChannelFuture,用于在异步执行操作的时候使用。

ChannelFuture提供了几种额外的方法,这些方法使得我们能够注册一个或者多个ChannelFutureListener实例。监听器的回调方法operationComplete(),将会在对应的操作完成时被调用。然后监听器可以判断该操作是成功地完成了还是出错了。由ChannelFutureListener提供的通知机制消除了手动检查对应操作是否完成的必要。每个Netty的出站I/O操作都将返回一个ChannelFuture;

EventLoop

EventLoop定义了Netty的核心抽象,用来处理连接的生命周期中所发生的事件,在内部,会为每个Channel分配一个EventLoop。

EventLoop 定义了Netty的核心抽象,用来处理连接的生命周期中所发生的事件,在内部,将会为每个Channel分配一个EventLoop。

EventLoop本身只由一个线程驱动,其处理了一个Channel的所有I/O事件,并且在该EventLoop的整个生命周期内都不会改变。这个简单而强大的设计消除了你可能有的在ChannelHandler实现中需要进行同步的任何顾虑。

 

这里需说到,EventLoop的管理是通过EventLoopGroup来实现的。还要一点要注意的是,客户端引导类是 Bootstrap,只需要一个EventLoopGroup。服务端引导类是 ServerBootstrap,通常需要两个 EventLoopGroup,一个用来接收客户端连接,一个用来处理 I/O 事件(也可以只使用一个 EventLoopGroup,此时其将在两个场景下共用同一个 EventLoopGroup)。

 

  • 一个 EventLoopGroup 包含一个或者多个 EventLoop;
  • 一个 EventLoop 在它的生命周期内只和一个 Thread 绑定;
  • 所有由 EventLoop 处理的 I/O 事件都将在它专有的Thread 上被处理;
  • 一个 Channel 在它的生命周期内只注册于一个EventLoop;
  • NIO中,一个 EventLoop 分配给多个 Channel(面对多个Channel,一个 EventLoop 按照事件触发,顺序执行); OIO中,一个 EventLoop 分配给一个 Channel。

总结

开头我们先介绍了Netty的概念,它是基于NIO的异步事件驱动的网络应用框架,接着列举了Netty的优点,并发性高,支持多种传输类型等,最后简单介绍了Netty的几大核心组件,让大家有了一个整体的简单认识,大家可能对这些组件概念不是很清晰,在往后的文章我会一一详细介绍了每个组件。