Netty 3 - 组件和设计

这里将回顾我们之前章节讲到过的主要概念和组件。

1 Channel 、EventLoop和ChannelFuture

  • Channel —— Socket;
  • EventLoop —— 控制流、多线程处理、并发;
  • ChannelFuture —— 异步通知。

1.1 Channel 接口

基本的I/O操作(bind()、connect()、read()和write())依赖于底层网络传输所提供的原语。在基于Java的网络编程中,其基本的构造是Class Socket。Netty的Channel接口所提供的API,大大降低了直接使用Socket类的复杂性。此外,Channel也是拥有许多预定义的、专门化实现的广泛类层次结构的根,下面是一个简短的部分清单:

  • EmbeddedChannel
  • LocalServerChannel
  • NioDatagramChannel
  • NioSctpChannel
  • NioSockectChannel

1.2 EventLoop接口

EventLoop定义了Netty的核心抽象,用于处理连接的声明周期中所发生的事件。下图在高层次上说明Channel、EventLoop、Thread以及EventLoopGroup之间的关系。

  • 一个EventLoopGroup包含一个或者多个EventLoop
  • 一个EventLoop在它的生命周期内只和一个Thread绑定
  • 所有由EventLoop处理的I/O事件都将在它专有的Thread上被处理
  • 一个Channel在它的生命周期内只注册于一个EventLoop
  • 一个EventLoop可能会被分配给一个或多个Channel

注意,在这种设计中,一个给定Channel的I/O操作都是由相同的Thread执行的,实际上消除了对于同步的需要。

1.3 ChannelFuture接口

Netty中所有的I/O操作都是异步的。因为一个操作可能不会立即返回,所以我们需要一种用于在之后的某个时间点确定其结果的方法。为此,Netty提供了ChannelFuture接口,其addListener()方法注册了一个ChannelFutureListener,以便于在某个操作完成时(无论是否成功)得到通知。

可以将ChannelFuture看作将来要执行的操作的结果的占位符。它究竟什么时候被执行则可能取决于若干的因素,因此不可能准确地预测,但是可以肯定的是它将会被执行。此外,所有属于同一个Channel的操作都被保证其将它们被调用的顺序执行。

2 ChannelHandler 和 channelPipeline

我们将更加细致地看一看那些管理数据流以及执行应用程序处理逻辑的组件。

2.1 ChannelHandler接口

从应用程序开发人员的角度来看,Netty的主要组件是ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器。实际上,ChannelHandler可专门用于几乎任何类型的动作,如将数据从一种格式转换为另一种格式,或处理转换过程中排除的异常。

其中,ChannelInboundHandler是开发人员经常实现的子接口,可以接收入站事件和数据。这些数据在随后将会被应用程序的业务逻辑所处理。

2.2 ChannelPipeline接口

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

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

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

ChannelHandler是专为支持广泛的用途而设计的,可以将它看作处理往来ChannelPipeline事件(包括数据)的任何代码的通用容器。类图如下所示:

ChannelPipeline是ChannelHandler的编排顺序。下图将说明Netty应用程序中入站和出站数据流之间的区别。从客户端角度看,如果事件的运动方向是从客户端到服务端,那么我们称这些事件为出站,反之则为入站。

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

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

关于入站和出站channelHandler的更多讨论

通过使用作为参数传递到每一个方法的ChannelHandlerContext,事件可以被传递给当前ChannelHandler链中的下一代ChannelHandler。因为你有时候会忽略那些不感兴趣的事件,所以Netty提供了抽象基类ChannelInboundHandlerAdapter和ChannelOutHandlerAdapter。通过调用ChannelHandlerContext上的对应方法,每个都提供了简单地将事件传递给下一个ChannelHandler的方法实现。随后,你可以通过重写你感兴趣的那些方法来扩展这些类。

鉴于出站操作和入站操作是不同的,如果两个类别的ChannelHandler都混合添加到同一个ChannelPipeline中会发生什么?虽然ChannelInboundHandler和ChannelOutboundHandler都扩展自ChannelHandler,但是Netty能区分ChannelInboundHandler实现和ChannelOutboundHandler实现,并确保数据只会在具有相同定向类型的两个ChannelHandler之间传递。

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

在Netty中,有两种发送消息的方式。

  1. 直接写到Channel中,导致信息从ChannelPipeline的尾端开始流动。
  2. 写到和ChannelHandler相关联的ChannelHandlerContext对象中,导致消息从ChannelPipeline中的下一个ChannelHandler开始流动。

2.3 更加深入地了解ChannelHandler

ChannelPipeline中的每个ChannelHandler将负责把事件转发到链中的下一个ChannelHandler.这些适配器类(及它们的子类)将自动执行这个操作,所以你可以只重写那些你想要特殊处理的方法和事件。

为什么需要适配器类

有一些适配器类可以编写自定义的ChannelHandler所需要的努力降到最低限度,因为他们提供了定义在对应接口中的所有方法的默认实现。

下面这些是编写自定义ChannelHandler时经常会用到的适配器类:

  • ChannelHandlerAdapter
  • ChannelInboundHandlerAdapter
  • ChannelOutboundHandlerAdapter
  • ChannelDuplexHandler

我们将研究3个ChannelHandler的子类型:编码器、解码器和SimpleChannelInboundHandler<T>-ChannelInboundHandlerAdapter的一个子类。

2.4 编码器和解码器

 当程序员通过Netty发送或者接收一个消息的时候,就将会发生一次数据转换。入站消息会被解码。也就是说,从字节转换为另一种格式,通常是一个Java对象。如果是出站消息,则会发生相反方向的转换:它将从它的当前格式被编码为字节。

这两种方向的转换原因,即网络数据总是一系列的字节。

对应于特定的需求, Netty为编码器和解码器提供了不同类型的抽象类。如,你的应用程序可能使用了一个中间格式,而不需要立即将消息转换成字节。你将仍然需要一个编码器,但是它将派生自一个不同的超类。为了确定合适的编码器类型,你可以应用一个简单的命名约定。

通常来说,这些基类的名称类似于ByteToMessageDecoder或MessageToByteEncoder。对于特殊的类型,你可能会发现类似于ProtobufEncoder和ProtobufDecoder这样的名称——预置的用来支持Google的Protocol Buffers。

严格说来,其他的处理器也能够完成编码器和解码器的功能,即Netty提供的编码器/解码器适配器类都实现了ChannelOutboundHandler或ChannelInboundHandler接口。

2.5 抽象类SimpleChannelInboundHandler

当我们要创建一个ChannelHandler来接收解码消息时,可以扩展基类SimpleChannelInboundHandler<T>,其中T是要处理的消息的Java类型。在这个ChannelHandler中将需要重写基类的一个或者多个方法,并且获取一个到ChannelHandlerContext的引用,这个引用将作为输入参数传递给ChannelHandler的所有方法。

基于SimpleChannelInboundHandler实现的Handler,最重要的方法是 channelRead0(ChannelHandlerContext,T)。除了要求不阻塞当前的I/O线程之外,其具体实现完全取决于开发者。

3 引导

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

面向连接的协议:严格来说,“连接”这个术语仅适用于面向连接的协议,如TCP,其保证了两个连接端点之间消息的有序传递。

因此有两种类型的引导,Bootstrap可用于引导客户端,ServerBootstrap可用于引导服务器。如下所示,比较了这两种类型的引导类。

类别BootstrapServerBootstrap
网络编程中的作用连接到远程主机和端口绑定到一个本地端口
EventLoopGroup的数目12

ServerBootstrap绑定到了一个端口,因此服务器必须要监听连接,而Bootstrap则是由想要连接到远程节点的客户端应用程序所使用。引导一个客户端只需要一个EventLoopGroup,但ServerBootStrap则需要两个。

为啥服务端需要两个EventLoopGroup?

是因为服务端需要两组不同的channel。第一组将只包含一个ServerChannel,代表服务器自身的已绑定到某个本地端口的正在监听的套接字。而第二组将包含所有已创建的用来处理传入客户端连接的channel。以下图将说明服务端为啥需要两个不同的EventLoopGroup。

4 总结

这里我们从技术和体系结构两个角度探讨了Netty的重要性,也更加详细地介绍了一些概念和组件,特别是对ChannelHandler、ChannelPipeline和引导等概念做了阐述。

另外,我们讨论了ChannelHandler类的层次结构,对编码器/解码器做了介绍,并描述了他们在数据和网络字节格式之间来回转换的互补功能。

后面的文章将更深入介绍这些组件,并剖析原理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/796269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【嵌入式开发 Linux 常用命令系列 4.3 -- git add 不 add untracked file】

请阅读【嵌入式开发学习必备专栏 】 文章目录 git add 不add untracked file git add 不add untracked file 如果你想要Git在执行git add .时不添加未跟踪的文件&#xff08;untracked files&#xff09;&#xff0c;你可以使用以下命令&#xff1a; git add -u这个命令只会加…

boost共享内存使用(3)managed_shared_memory共享内存分配器

文章目录 概述使用示例 概述 Boost.Interprocess提供了一些基本的类来创建共享内存对象和文件映射&#xff0c;并将这些可映射的类映射到进程的地址空间中。 然而&#xff0c;管理这些内存段对于非平凡的任务来说并不容易。一个映射区域是一个固定长度的内存缓冲区&#xff0…

免注册,ChatGPT可即时访问了!

AI又有啥进展&#xff1f;一起看看吧 Apple进军个人家用机器人 Apple在放弃自动驾驶汽车项目并推出混合现实头显后&#xff0c;正在进军个人机器人领域&#xff0c;处于开发家用环境机器人的早期阶段 报告中提到了两种可能的机器人设计。一种是移动机器人&#xff0c;可以跟…

鸿蒙OS元服务开发:【(Stage模型)学习窗口沉浸式能力】

一、体验窗口沉浸式能力说明 在看视频、玩游戏等场景下&#xff0c;用户往往希望隐藏状态栏、导航栏等不必要的系统窗口&#xff0c;从而获得更佳的沉浸式体验。此时可以借助窗口沉浸式能力&#xff08;窗口沉浸式能力都是针对应用主窗口而言的&#xff09;&#xff0c;达到预…

二叉堆解读

在数据结构和算法中&#xff0c;二叉堆是一种非常重要的数据结构&#xff0c;它被广泛用于实现优先队列、堆排序等场景。本文将介绍二叉堆的基本概念、性质、操作以及应用场景。 一、基本概念 二叉堆是一种特殊的完全二叉树&#xff0c;它满足堆性质&#xff1a;对于每个节点…

练习题(2024/4/3)

1题目描述&#xff1a; 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,3], nums2 [2] 输出&…

Redis Hash结构操作

基础篇Redis 6.4 Hash结构操作 在基础篇的最后&#xff0c;咱们对Hash结构操作一下&#xff0c;收一个小尾巴&#xff0c;这个代码咱们就不再解释啦 马上就开始新的篇章~~~进入到我们的Redis实战篇 SpringBootTest class RedisStringTests {Autowiredprivate StringRedisTe…

电子商务平台中大数据的应用|主流电商平台大数据采集API接口

(一)电商平台物流管理中大数据的应用 电商平台订单详情订单列表物流信息API接口应用 电子商务企业对射频识别设备、条形码扫描设备、全球定位系统及销售网站、交通、库存等管理软件数据进行实时或近实时的分析研究,提高物流速度和准确性。部分电商平台已建立高效的物流配送网…

什么是Java中的分布式系统?举例说明

在Java中&#xff0c;分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。这种系统架构的目的是利用更多的机器处理更多的数据&#xff0c;从而解决单个计算机无法应对的计算、存储任务。 当单个节点的处理能力无法满足日益增长的计算…

【STL】vector的底层原理及其实现

vector的介绍 vector是一个可变的数组序列容器。 1.vector的底层实际上就是一个数组。因此vector也可以采用连续存储空间来存储元素。也可以直接用下标访问vector的元素。我们完全可以把它就当成一个自定义类型的数组使用。 2.除了可以直接用下标访问元素&#xff0c;vector还…

掌握数据相关性新利器:基于R、Python的Copula变量相关性分析及AI大模型应用探索

在工程、水文和金融等各学科的研究中&#xff0c;总是会遇到很多变量&#xff0c;研究这些相互纠缠的变量间的相关关系是各学科的研究的重点。虽然皮尔逊相关、秩相关等相关系数提供了变量间相关关系的粗略结果&#xff0c;但这些系数都存在着无法克服的困难。例如&#xff0c;…

使用预训练的bert large model实现问答系统源码(本地实现 question answer system)

pre-trained bert model 预训练好的Bert模型 本地实现问答系统 用这条命令将bert下载到本地&#xff1a; model.save_pretrained("path/to/model") 具体代码 如下链接&#xff1a; https://download.csdn.net/download/qqqweiweiqq/89092005

解决win7作为虚拟机无法复制粘贴共享文件的问题

win7作为虚拟机经常会出现无法与主机的剪切板共享、文件共享。 归根结底是win7虚拟机里面没有安装VMware Tools 能够成功安装vmware tools的条件&#xff1a; 1&#xff09;win7版本为win7 sp1及以上 2&#xff09;安装KB4490628&#xff0c;KB4474419补丁 因此下面来详细介绍…

【LeetCode题解】2192. 有向无环图中一个节点的所有祖先+1026. 节点与其祖先之间的最大差值

文章目录 [2192. 有向无环图中一个节点的所有祖先](https://leetcode.cn/problems/all-ancestors-of-a-node-in-a-directed-acyclic-graph/)思路&#xff1a;BFS记忆化搜索代码&#xff1a; 思路&#xff1a;逆向DFS代码&#xff1a; [1026. 节点与其祖先之间的最大差值](https…

在MacOS上安装Homebrew:初学者指南

简介&#xff1a; 如果您是MacOS的新手或者不了解Homebrew是什么&#xff0c;那么本文是给您准备的。Homebrew是一个MacOS上的包管理器&#xff0c;它可以让您在几个简单的步骤中安装和管理数千个软件包。在本文中&#xff0c;我们将向您介绍如何在MacOS上安装Homebrew&#x…

为什么说AI的尽头是生物制药?

AI的尽头究竟是什么&#xff1f;有投资者说是光伏&#xff0c;也有投资者说是电力&#xff0c;而英伟达给出的答案则是生物制药。 在英伟达2023年投资版图中&#xff0c;除AI产业根基算法与基础建设外&#xff0c;生物制药是其重点布局的核心赛道。英伟达医疗保健副总裁Kimber…

FastEI论文阅读

前言 研究FastEI&#xff08;Ultra-fast and accurate electron ionization mass spectrum matching for compound identification with million-scale in-silico library&#xff09;有很长时间了&#xff0c;现在来总结一下&#xff0c;梳理一下认知。PS&#xff1a;为什么要…

【LeetCode: 21. 合并两个有序链表 + 链表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

组件循环依赖问题

场景&#xff1a; a组件 引入 b组件 b组件 引入 a组件 a组件 import B from ./b console.log(A组件&#xff0c;,B) export default Ab组件 import A from ./a console.log(B组件&#xff0c;,A) export default B输出结果&#xff1a; B组件&#xff0c;undefined A组件&am…

axios快速入门

一、环境配置 1.1概述 上古浏览器页面在向服务器请求数据时&#xff0c;因为返回的是整个页面的数据&#xff0c;页面都会强制刷新一下&#xff0c;这对于用户来讲并不是很友好。并且我们只是需要修改页面的部分数据&#xff0c;但是从服务器端发送的却是整个页面的数据&#…