第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
英文地址,請注明出處:http://en.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;采用有意義的變量名進行封裝、將復雜的判斷拆解為獨立的函數、優先使用“肯定式”而非“否定式”邏輯、利用括號明確運算的優先級、以及運用德摩根定律…