第6章 Decoder与Encoder核心组件

前言

Netty从底层Java通道读取ByteBuf二进制数据,传入Netty通道的流水线,随后开始入站处理。在入站处理过程中,需要将ByteBuf二进制类型解码成Java POJO对象。这个解码过程可以通过Netty的Decoder(解码器)去完成。

在出站处理过程中,业务处理后的结果(出站数据)需要从某个Java POJO对象编码为最终的ByteBuf二进制数据,然后通过底层Java通道发送到对端。在编码过程中,需要用到Netty的Encoder(编码器)去完成数据的编码工作。

解码器:入站处理过程中,将ByteBuf二进制类解码为Java POJO对象;
编码器:出站处理过程中,将Java POJO对象编码为ByteBuf二进制数据。

Decoder原理与实战

Netty解码器是什么?
(1)它是一个InBound入站处理器,负责处理“入站数据”。
(2)它能将上一站Inbound入站处理器传过来的输入(Input)数据进行解码或者格式转换,然后发送到下一站Inbound入站处理器。

一个标准的解码器的职责为:将输入类型为ByteBuf的数据进行解码,输出一个一个的Java POJO对象。Netty内置了yteToMessageDecoder解码器。

Netty中的解码器都是Inbound入站处理器类型,都直接或者间接地实现了入站处理的超级接口ChannelInboundHandler。

ByteToMessageDecoder解码器处理流程

ByteToMessageDecoder是一个非常重要的解码器基类,是一个抽象类,实现了解码处理的基础逻辑和流程。ByteToMessageDecoder继承自ChannelInboundHandlerAdapter适配器,是一个入站处理器,用于完成从ByteBuf到Java POJO对象的解码功能。

ByteToMessageDecoder解码的流程大致如图所示。
在这里插入图片描述

自定义Byte2IntegerDecoder整数解码器

Byte2IntegerDecoder.java

由于解码器的功能仅仅是完成ByteBuf的解码,不做其他业务处理,所以还需要编写一个业务处理器,用于在读取解码后的Java POJO对象之后完成具体的业务处理。
IntegerProcessHandler.class

ReplayingDecoder解码器

使用上面的Byte2IntegerDecoder整数解码器会面临一个问题:需要对ByteBuf的长度进行检查,有足够的字节才能进行整数的读取。这种长度的判断是否可以由Netty来帮忙完成呢?答案是可以的,可以使用Netty的ReplayingDecoder类省去长度的判断。

ReplayingDecoder对输入的ByteBuf进行了“偷梁换柱”,在将外部传入的ByteBuf缓冲区传给子类之前,换成了自己装饰过的ReplayingDecoderBuffer缓冲区。也就是说,在示例程序中,Byte2IntegerReplayDecoder中的decode()方法所得到的实参in的直接类型并不是原始的ByteBuf类型,而是ReplayingDecoderBuffer类型。

实质上,ReplayingDecoder的作用远远不止于进行长度判断,它更重要的作用是用于分包传输的应用场景。

整数的分包解码器的实战案例

通道接收到的ByteBuf数据包和发送端发送的数据包不完全一致:
在这里插入图片描述

Netty通过什么样的解码器对图中接收端的3个ByteBuf缓冲数据进行解码,而后得到和发送端一模一样的4个字符串呢?理论上可以使用ReplayingDecoder来解决。在进行数据解析时,如果发现当前ByteBuf中所有可读的数据不够,那么ReplayingDecoder会一直等待,直到可读数据是足够的。这一切都是在ReplayingDecoder内部,通过与缓冲区装饰器ReplayingDecoderBuffer相互配合完成的。

Byte2IntegerReplayDecoderTester

字符串的分包解码器的实战案例

在原理上,字符串分包解码和整数分包解码是一样的,所不同的是:整数的长度是固定的,目前在Java中是4字节;字符串的长度是不固定的,是可变的。

如何获取字符串的长度信息呢?这是一个小小的难题,和程序所使用的具体传输协议是强相关的。一般来说,在Netty中进行字符串的传输可以采用普通的Head-Content内容传输协议。该协议的规则很简单:
(1)在协议的Head部分放置字符串的字节长度,可以用一个整数类型来描述。
(2)在协议的Content部分,放置字符串的字节数组。

MessageToMessageDecoder解码器

与前面不同的是,解码器需要继承一个新的Netty解码器基类MessageToMessageDecoder<I>。在继承它的时候,需要明确的泛型实参<I>,用于指定入站消息的Java POJO类型。
为什么继承MessageToMessageDecoder<I>时需要指定入站数据的类型,而在前面继承ByteToMessageDecoder解码ByteBuf时不需要指定泛型实参呢?原因很简单:ByteToMessageDecoder的入站消息类型是十分明确的,就是二进制缓冲区ByteBuf类型;MessageToMessageDecoder<I>的入站消息类型是不明确的,可以是任何POJO类型,所以需要指定。

常用的内置Decoder

Netty提供了不少开箱即用的Decoder(解码器),能够满足很多编解码应用场景的需求。
(1)固定长度数据包解码器——FixedLengthFrameDecoder
(2)行分割数据包解码器——LineBasedFrameDecoder
(3)自定义分隔符数据包解码器——DelimiterBasedFrameDecoder
(4)自定义长度数据包解码器——LengthFieldBasedFrameDecoder

LineBasedFrameDecoder解码器

LineBasedFrameDecoder,它是一个最为基础的Netty内置解码器。这个解码器的工作原理很简单,依次遍历ByteBuf数据包中的可读字节,判断在二进制字节流中是否存在换行符"\n"或者"\r\n"的字节码。如果有,就以此位置为结束位置,把从可读索引到结束位置之间的字节作为解码成功后的ByteBuf数据包。

LineBasedFrameDecoder,它是一个最为基础的Netty内置解码器。这个解码器的工作原理很简单,依次遍历ByteBuf数据包中的可读字节,判断在二进制字节流中是否存在换行符"\n"或者"\r\n"的字节码。如果有,就以此位置为结束位置,把从可读索引到结束位置之间的字节作为解码成功后的ByteBuf数据包。

DelimiterBasedFrameDecoder解码器

DelimiterBasedFrameDecoder解码器不仅可以使用换行符,还可以使用其他特殊字符作为数据包的分隔符,例如制表符"\t"。

LengthFieldBasedFrameDecoder解码器

传输内容中的Length(长度)字段的值是指存放在数据包中要传输内容的字节数。普通的基于Head-Content协议的内容传输尽量用内置的LengthFieldBasedFrameDecoder来解码。
在这里插入图片描述

多字段Head-Content协议数据包解析的实战案例

NettyOpenBoxDecoder
在这里插入图片描述

Encoder原理与实战

在Netty的业务处理完成后,业务处理的结果往往是某个Java POJO对象需要编码成最终的ByteBuf二进制类型,通过流水线写入底层的Java通道,这就需要用到Encoder(编码器)。

在Netty中,什么叫编码器?首先,编码器是一个Outbound出站处理器,负责处理“出站”数据;其次,编码器将上一站Outbound出站处理器传过来的输入(Input)数据进行编码或者格式转换,然后传递到下一站ChannelOutboundHandler出站处理器。

MessageToByteEncoder编码器

MessageToByteEncoder是一个非常重要的编码器基类,位于Netty的io.netty.handler.codec包中。MessageToByteEncoder的功能是将一个Java POJO对象编码成一个ByteBuf数据包。

Integer2ByteEncoderTester

MessageToMessageEncoder编码器

能够通过Netty的编码器将某种POJO对象编码成另外一种POJO对象呢?答案是肯定的。需要继承另外一个Netty的重要编码器——MessageToMessageEncoder编码器,并实现它的encode()抽象方法。在子类的encode()方法实现中,完成原POJO类型到目标POJO类型的转换逻辑。在encode()实现方法中,编码完成后,将解码后的目标对象加入encode()方法中的实参list输出容器即可。

解码器和编码器的结合

在实际的开发中,由于数据的入站和出站关系紧密,因此编码器和解码器的关系很紧密。

前面讲到编码器和解码器是分开实现的。例如,通过继承ByteToMessageDecoder基类或者其子类,完成ByteBuf数据包到POJO的解码工作;通过继承基类MessageToByteEncoder或者其子类,完成POJO到ByteBuf数据包的编码工作。总之,具有相反逻辑的编码器和解码器分开实现在两个不同的类中,导致的一个结果是相互配套的编码器和解码器在加入通道的流水线时常常需要分两次添加。

ByteToMessageCodec编解码器

现在的问题是:具有相互配套逻辑的编码器和解码器能否放在同一个类中呢?答案是肯定的,这需要用到Netty的新类型——Codec(编解码器)。

编解码器ByteToMessageCodec同时包含了编码encode()和解码decode()两个抽象方法,这两个方法都需要我们自己实现:
(1)编码方法——encode(ChannelHandlerContext, I,ByteBuf)。
(2)解码方法——decode(ChannelHandlerContext, ByteBuf,List)。

CombinedChannelDuplexHandler组合器

前面的编码器和解码器相结合是通过继承完成的。继承的不足之处在于:将编码器和解码器的逻辑强制性地放在同一个类中,在只需要编码或者解码单边操作的流水线上,逻辑上不大合适。

编码器和解码器如果要结合起来,除了继承的方法之外,还可以通过组合的方式实现。与继承相比,组合会带来更大的灵活性:编码器和解码器可以捆绑使用,也可以单独使用。

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

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

相关文章

[已解决]当启动 Spring Boot 应用时出现 Using generated security password xxx提示

当启动 Spring Boot 应用时出现 Using generated security password xxx提示当启动 Spring Boot 应用时出现 Using generated security password xxx提示&#xff0c;这是 Spring Security 自动配置的默认行为&#xff0c;通常发生在你​​未自定义安全配置​​但引入了 Spring…

自动分析需求,PRD 生成只需 SOLO 一步!

资料来源&#xff1a;火山引擎-开发者社区 写不清需求&#xff1f;PRD 难产&#xff1f;开发总跑偏&#xff1f;这些痛点&#xff0c;SOLO 来解决。 TRAE SOLO 是行业首个 Context Engineer。它不止协助编码&#xff0c;更能基于精准上下文理解和工具调用&#xff0c;从构思、…

物联网软件开发过程中,数据流图(DFD),用例图,类图,活动图,序列图,状态图,实体关系图(ERD),BPMN(业务流程建模)详解分析

概述软件开发过程中&#xff0c;特别是在物联网&#xff08;IoT&#xff09;场景中&#xff0c;数据流图&#xff08;DFD&#xff09;、UML图&#xff08;包括用例图、类图、活动图、序列图、状态图&#xff09;、实体关系图&#xff08;ERD&#xff09;和业务流程建模&#xf…

Mac(一)常用的快捷键整理

目录1、系统操作与窗口管理2、应用与窗口切换3、常规编辑操作4、文本导航与光标控制✏️5、文本格式与文档功能&#xff08;支持应用中&#xff09;6、截图快捷键7、Safari 浏览器快捷键8、Finder 快捷键&#xff08;文件管理&#xff09;9、Fn / Globe 功能键&#xff08;部分…

HAProxy使用方法以及和LVS区别

HAProxy简介HAProxy是法国开发者 威利塔罗(Willy Tarreau) 在2000年使用C语言开发的一个开源软件 是一款具备高并发(万级以上)、高性能的TCP和HTTP负载均衡器 支持基于cookie的持久性&#xff0c;自动故障切换&#xff0c;支持正则表达式及web状态统计LVS 与 HAProxy 的核心区别…

超越“小作文”:大模型指令设计的进阶之路——优化知识信噪比

文章摘要&#xff1a;你是否认为&#xff0c;给大模型的指令&#xff08;Prompt&#xff09;写得越详细越好&#xff1f;真的是信息越多&#xff0c;模型就越懂你吗&#xff1f;本文将深入探讨一个反直覺的觀點&#xff1a;初級的指令設計專注於資訊的堆砌&#xff0c;而高階的…

elasticsearch-集成prometheus监控(k8s)

一. 简介&#xff1a; 关于elasticsearch的简介和部署&#xff0c;可以参考单独的文章elasticsearch基础概念与集群部署-CSDN博客&#xff0c;这里就不细说了。这里只讲讲如何在k8s中部署export并基于prometheus做es的指标采集。 二. 实现方式&#xff1a; 首先我们需要先部署…

贪心算法(Greedy Algorithm)详解

一、什么是贪心算法&#xff1f; 贪心算法是一种算法设计范式&#xff0c;指在解决问题时&#xff0c;依赖于每次选择最优的局部解&#xff0c;以期最终得到全局最优解。贪心算法的关键特点是&#xff1a; 局部最优选择&#xff1a;每个阶段选择当前看起来最好的选择&#xff0…

电梯的构造|保养|维修视频全集_电梯安全与故障救援(课程下载)

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/91699586 电梯原理与维修视频教程 相关简介: 电梯现在运用的非常广泛,比如大型商场,建筑工地,特别是现在建造的很多高楼、商品房,基本都是安装了电梯。电梯维保不力是导致电梯运行中安全事故频发的主要原…

Traefik网关DNS解析超时问题优化

1、背景 在生产环境使用 Traefik 网关时出现了偶发的 DNS 解析超时导致网关与后端服务建立连接异常的情况。通过调用链埋点数据观察发现&#xff0c;该部署环境中 Traefik 的 DNS 解析性能较差&#xff0c;耗时通常在 4ms 以上&#xff08;正常应该是 1ms 以内&#xff09; 初…

从0到1掌握 Spring Security(第三篇):三种认证方式,按配置一键切换

> 本文是Spring Security系列第三篇,将带你实现内存、JDBC和自定义三种认证方式的无缝切换,只需修改配置文件即可完成认证策略变更! ## 一、为什么需要多种认证方式? 在软件开发的不同阶段,我们需要不同的认证策略: - **开发阶段**:使用内存认证,快速配置测试账号…

阿里云国际站云防火墙:如何利用阿里云云防火墙实现细粒度的访问控制?

利用阿里云云防火墙实现细粒度的访问控制&#xff0c;可以从分层策略、精确匹配、动态调整三个方面着手&#xff0c;让不同业务、用户和资源的访问权限清晰可控。一、明确控制目标业务隔离&#xff1a;不同业务系统、部门或环境&#xff08;生产/测试&#xff09;之间互不干扰。…

rom定制系列------小米cc9机型 原生安卓15系统 双版线刷root 定制修改功能项

小米 9 Lite/CC9 机型代码;pyxis.搭载骁龙710处理器.适用于以下型号的小米机型&#xff1a;M1904F3BG, M1904F3BC. 刷写前提; 需要当前机型已经解锁bl的状态下进入fast模式刷写。此机型可以正常官方解锁与强解bl锁。效果都是一样的。在fast模式下装好联机驱动。使用官方平台刷…

解读60页全面认识大数据基础知识培训【附全文阅读】

该培训课件适用于对大数据知识感兴趣的初学者、企业管理人员、相关技术从业者等。内容围绕大数据展开,先介绍其基本概念,包括定义、数据级别、来源、类型、价值挖掘等,还阐述了 5 个 “V” 特征及与传统数据的区别。接着讲述大数据的发展演进,涵盖国际国内发展历程、发展阶…

Prompt engineering(PE) —— prompt 优化如何进行?

从新手到高手&#xff1a;Prompt最佳实践全解析 一、引言&#xff1a;开启 Prompt 的神秘大门在这个人工智能飞速发展的时代&#xff0c;AI 已经悄然融入我们生活的方方面面。你是否有过这样的经历&#xff1a; 当你对着智能音箱询问 “明天天气如何” 时&#xff0c;它能迅速给…

云服务器的优缺点都有哪些?

云服务器作为一种有着高度灵活性的服务器类型&#xff0c;能够根据用户的需求来调整资源&#xff0c;有着很强的优势&#xff0c;但是云服务器还是有着一定的缺点的&#xff0c;本文就来共同探讨一下云服务器的优缺点都有哪些吧&#xff01;首先&#xff0c;云服务器能根据业务…

宋红康 JVM 笔记 Day05|运行时数据区内部结构、JVM中的线程说明、程序计数器

一、今日视频区间 P39-P43 二、一句话总结 运行时数据区内部结构&#xff1b;JVM中的线程说明&#xff1b;程序计数器&#xff08;PC寄存器&#xff09;&#xff1b; 三、关键图/命令 3.1 运行时数据区内部结构3.2 JVM中的线程说明3.3 程序计数器&#xff08;PC寄存器&#xff…

Java增强for循环(小白友好版)

前言&#xff1a;为什么需要增强for循环&#xff1f;作为Java初学者&#xff0c;你或许已经学会使用传统for循环来遍历数组或集合&#xff1a;for (int i 0; i < array.length; i) {System.out.println(array[i]); }这种写法需要手动维护索引变量i&#xff0c;对于集合还需…

【OLAP】trino安装和基本使用

目录 ​一、概述 1.1Trino不是什么 1.2Trino是什么 二、Trino特点 三、Trino架构 3.1架构和服务节点 3.2Trino数据模型 四、Trino安装部署 4.1配置JDK 4.2单机版&#xff08;Coordinator和Worker同进程&#xff09; 4.2.1启动服务 4.2.2下载客户端 五、配置HTTPS&…

如何写出更清晰易读的布尔逻辑判断?

列编码技巧和规范&#xff0c;来降低逻辑的“认知负荷”。成功的实践&#xff0c;必须系统性地涵盖五大关键策略&#xff1a;采用有意义的变量名进行封装、将复杂的判断拆解为独立的函数、优先使用“肯定式”而非“否定式”逻辑、利用括号明确运算的优先级、以及运用德摩根定律…