首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Netty|工作流程图分析&核心组件说明&代码案例实践

一、Netty 工作流程

我们先来看看Netty的工作原理图,简单说一下工作流程,然后通过这张图来一一分析Netty的核心组件。

1.1、Server工作流程图:

1.2、Server工作流程分析:

server端启动时绑定本地某个端口,初始化.

将自己注册到某个的selector上。

server端包含1个和1个,

专门负责接收客户端的连接,专门负责网络的读写

NioEventLoopGroup相当于1个事件循环组,这个组里包含多个事件循环NioEventLoop,每个NioEventLoop包含1个selector和1个事件循环线程。

循环执行的任务:

1、轮询accept事件;

2、处理accept事件,将生成的NioSocketChannel注册到某一个的Selector上。

3、处理任务队列中的任务,runAllTasks。任务队列中的任务包括用户调用执行的任务,或者其它线程提交到该的任务。

循环执行的任务:

轮询事件

处理IO事件,在NioSocketChannel可读、可写事件发生时,回调(触发)ChannelHandler进行处理。

处理任务队列的任务,即

1.3、Client工作流程图

流程就不重复概述啦

二、核心模块组件

Netty的核心组件大致是以下几个:

2.1、Channel 接口

我们平常用到基本的 I/O 操作(bind()、connect()、read()和 write()),其本质都依赖于底层网络传输所提供的原语,在Java中就是类。

Netty 的 Channel 接 口所提供的 API,大大地降低了直接使用 类的复杂性。另外 提供异步的网络 操作(如建立连接,读写,绑定端口),异步调用意味着任何 调用都将立即返回,并且不保证在调用结束时所请求的 操作已完成。

在调用结束后立即返回一个 实例,通过注册监听器到 上,支持 在 操作成功、失败或取消时立马回调通知调用方。

此外,Channel 也是拥有许多预定义的、专门化实现的广泛类层次结构的根,比如:

:用于本地传输的ServerChannel ,允许 VM 通信。

:以嵌入式方式使用的 Channel 实现的基类。

:异步的客户端  TCP 、Socket 连接。

:异步的服务器端  TCP、Socket 连接。

:异步的  UDP 连接。

:异步的客户端 Sctp 连接,它使用非阻塞模式并允许将 SctpMessage 读/写到底层 SctpChannel。

:异步的 Sctp 服务器端连接,这些通道涵盖了 UDP 和 TCP 网络 IO 以及文件 IO。

2.2、EventLoopGroup接口

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

Netty 通过触发事件将 Selector 从应用程序中抽象出来,消除了所有本来将需要手动编写 的派发代码。在内部,将会为每个 Channel 分配一个 EventLoop,用以处理所有事件,包括:

注册事件;

将事件派发给 ChannelHandler;

安排进一步的动作。

不过在这里我们不深究它,针对 Channel、EventLoop、Thread 以及 EventLoopGroup 之间的关系做一个简单说明。

一个 包含一个或者多个 ;

每个 维护着一个 实例,所以一个 EventLoop 在它的生命周期内只和一个 绑定;

因此所有由 处理的 I/O 事件都将在它专有的 上被处理,实际上消除了对于同步的需要;

一个 在它的生命周期内只注册于一个 ;

一个 可能会被分配给一个或多个。

通常一个服务端口即一个 对应一个 和一个 线程。 负责接收客户端的连接并将 交给 来进行 IO 处理,就如上文中的流程图一样。

2.3、ChannelFuture 接口

Netty 中所有的 I/O 操作都是异步的。因为一个操作可能不会 立即返回,所以我们需要一种用于在之后的某个时间点确定其结果的方法。具体的实现就是通过 和 ,其 方法注册了一个 ,以便在某个操作完成时(无论是否成功)自动触发注册的监听事件。

常见的方法有

,返回当前正在进行 操作的通道

,等待异步操作执行完毕

2.4、ChannelHandler 接口

从之前的入门程序中,我们可以看到在Netty中的重要性,它充当了所有处理入站和出站数据的应用程序逻辑的容器。我们的业务逻辑也大都写在实现的字类中,另外 的方法是由事件自动触发的,并不需要我们自己派发。

的实现类或者实现子接口有很多。平时我们就是去继承或子接口,然后重写里面的方法。

最常见的几种Handler:

:接收入站事件和数据

:用于处理出站事件和数据。

常见的适配器:

:用于处理入站IO事件

实现的抽象基类,它提供了所有方法的实现。这个实现只是将操作转发到的下一个。子类可以覆盖方法实现来改变这一

:用于处理出站IO事件

我们经常需要自定义一个 类去继承 ,然后通过重写相应方法实现业务逻辑,我们来看看有哪些方法可以重写:

2.5、ChannelPipeline 接口

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

一个Channel包含了一个 ,而ChannelPipeline中又维护了一个由 组成的双向链表,并且每个ChanneHandlerContext中又关联着一个。

安装到 中的过程:

一个的实现被注册到了中 ;

当 方法被调用时,ChannelInitializer将在 中安装一组自定义的 ;

将它自己从 中移除。

从一个客户端应用程序 的角度来看,如果事件的运动方向是从客户端到服务器端,那么我们称这些事件为出站的,反之 则称为入站的。服务端反之。

如果一个消息或者任何其他的入站事件被读取,那么它会从 的头部 开始流动,并被传递给第一个 。次此handler处理完后,数据将会被传递给链中的下一个 。最终,数据将会到达 的尾端,至此,所有处理就结束了。

出站事件会从尾端往前传递到最前一个出站的 handler。出站和入站两种类型的 handler互不干扰。

2.6、ChannelHandlerContext 接口

作用就是使能够与其和其他处理程序交互。因为 ChannelHandlerContext保存相关的所有上下文信息,同时关联一个 对象, 另外,可以通知的下一个以及动态修改它所属的。

2.7、SimpleChannelInboundHandler 抽象类

我们常常能够遇到应用程序会利用一个   来接收解码消息,并在这个Handler中实现业务逻辑,要写一个这样的 ,我们只需要扩展抽象类 即可, 其中T类型是我们要处理的消息的Java类型。

在中最重要的方法就是,

我们自己实现了这个方法之后,接收到的消息就已经被解码完的消息啦。

举个例子:

2.8、Bootstrap、ServerBootstrap 类

意思是引导,一个 应用通常由一个 开始,主要作用是配置整个 程序,串联各个组件。

我想大家对于最后一点可能会存有疑惑,为什么一个是1一个是2呢?

因为服务器需要两组不同的 。

第一组将只包含一个 ,代表服务 器自身的已绑定到某个本地端口的正在监听的套接字。

而第二组将包含所有已创建的用来处理传入客户端连接(对于每个服务器已经接受的连接都有一个)的 。

这一点可以上文中的流程图。

2.9、ChannelFuture  接口

异步 Channel I/O 操作的结果。Netty 中的所有 I/O 操作都是异步的。这意味着任何 I/O 调用将立即返回,但不保证在调用结束时请求的 I/O 操作已完成。相反,您将返回一个 ChannelFuture 实例,该实例为您提供有关 I/O 操作的结果或状态的信息。ChannelFuture 要么未完成,要么已完成。当 I/O 操作开始时,会创建一个新的未来对象。新的未来最初是未完成的——它既没有成功,也没有失败,也没有取消,因为 I/O 操作还没有完成。如果 I/O 操作成功完成、失败或取消,则使用更具体的信息(例如失败原因)将未来标记为已完成。请注意,即使失败和取消也属于完成状态。

中所有的 操作都是异步的,不能立刻得知消息是否被正确处理。但是可以过一会等它执行完成或者直接注册一个监听,具体的实现就是通过 和 ,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发注册的监听事件

常见的方法有

,返回当前正在进行 操作的通道

,等待异步操作执行完毕

2.10、ChannelOption 类

在创建 实例后,一般都需要设置 参数。

参数如下:

:一直保持连接状态

:对应TCP/IP协议listen 函数中的backlog参数,用来初始化服务器可连接队列大小。服务端处理客户端连接请求是顺序处理内,所N博求放在队刚中等待处理,backilog参数指定端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理, backlog参数指定了队列的大小。

三、应用实例

【案例】:

写一个服务端,两个或多个客户端,客户端可以相互通信。

3.1、服务端 Handler

的实现类或者实现子接口有很多。平时我们就是去继承或子接口,然后重写里面的方法。

在这里我们就是继承了 SimpleChannelInboundHandler< T > ,这里面许多方法都是大都只要我们重写一下业务逻辑,触发大都是在事件发生时自动调用的,无需我们手动调用。

3.2、服务端 Server 启动

3.3、客户端 Handler

3.4、客户端 Server

多个客户端,cv一下即可。

3.5、测试:

测试流程是先启动 服务端 Server,再启动客户端 。

四、自言自语

这篇文章应该算是个存稿了,之前忙其他事情去了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20211013A013K500?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券