最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Netty如何自定義編碼解碼器

 更新時(shí)間:2025年03月06日 10:35:31   作者:帥氣的人123  
Netty自定義編碼解碼器:InboundHandler處理入棧數(shù)據(jù),OutboundHandler處理出棧數(shù)據(jù),解碼器繼承ByteToMessageDecoder,編碼器繼承MessageToByteEncoder,ReplayingDecoder簡(jiǎn)化了解碼邏輯,但可能因異常重試導(dǎo)致性能下降

Netty 自定義編碼解碼器

入棧:InboundHandler ,出棧:OutboundHandler。Netty 構(gòu)建 chain 來(lái)處理業(yè)務(wù)。

自定義一個(gè)解碼器

解碼器主要是對(duì)客戶(hù)端發(fā)送的消息進(jìn)行解碼處理,所以他是一個(gè)入棧的處理器,因此他會(huì)有一個(gè)入棧的標(biāo)識(shí)ibound=true;

解碼器一般我都們都會(huì)基層 Netty 提供給的一個(gè)實(shí)現(xiàn)類(lèi)來(lái)實(shí)現(xiàn)自己的解碼邏輯 -> io.netty.handler.codec.ByteToMessageDecoder 這就是解碼的抽象類(lèi),默認(rèn)我們要實(shí)現(xiàn)一個(gè)抽象方法

com.netty.codec.custom.InboundAndOutboundHandler#decode 

這個(gè)方法有大概三個(gè)參數(shù);

  • ChannelHandlerContext channelHandlerContext 這個(gè)是上下文信息,可以獲取通道、管道等信息。
  • ByteBuf byteBuf 客戶(hù)端發(fā)送的消息就是存在這個(gè)參數(shù)的對(duì)象里面我們要通過(guò)這個(gè)對(duì)象的read***方法讀取我們需要的數(shù)據(jù)類(lèi)型,可以是 Long,Byte 等類(lèi)型的數(shù)據(jù)然后我們可以就可以轉(zhuǎn)換成為我們需要的格式。
  • List<Object> list 集合,將解碼后的數(shù)據(jù)傳遞給下一個(gè)inboundHandler 處理類(lèi)。

代碼示例

@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) {
    // Long => 8 byte
    if (byteBuf.readableBytes() >= 8) {
        list.add(byteBuf.readLong());
    } else log.warn("字節(jié)數(shù)異常 => {}", byteBuf.readableBytes());
}

這樣我們就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的解碼器。

自定義一個(gè)編碼器

解碼器主要是對(duì)服務(wù)端將要發(fā)送給客戶(hù)端的消息進(jìn)行編碼處理,所以他是一個(gè)出棧的處理器,因此他會(huì)有一個(gè)入棧的標(biāo)識(shí)outbound=true;

使用 Netty 提供的抽象類(lèi) => io.netty.handler.codec.MessageToByteEncoder<T> 泛型 T 表示你要發(fā)送的消息的類(lèi)型,實(shí)現(xiàn)抽象方法 => com.netty.codec.custom.OutboundHandler#encode 方法的參數(shù)有三個(gè):

  • ChannelHandlerContext channelHandlerContext 這個(gè)是上下文信息,可以獲取通道管道等信息。
  • Long msg 服務(wù)端要發(fā)送給客戶(hù)端的消息
  • ByteBuf byteBuf

代碼示例

@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Long msg, ByteBuf byteBuf) {
    byteBuf.writeLong(msg);
    log.info("發(fā)送消息成功");
}

后續(xù) -> io.netty.handler.codec.ReplayingDecoder

ByteToMessage 其實(shí)在使用過(guò)程中會(huì)遇到一些問(wèn)題,例如:

當(dāng)我們的解碼器中想要將字節(jié)轉(zhuǎn)換為一個(gè) Integer ,我們知道 Integer 是四個(gè)字節(jié)的,但是如果在讀取的時(shí)候不夠四個(gè)字節(jié),這個(gè)時(shí)候我們就需要做一些判斷邏輯 => if (byteBuf.readableBytes() >= 4) 當(dāng)這個(gè)返回值為 true 的時(shí)候我么就可以繼續(xù)執(zhí)行解碼的邏輯。

那我們?cè)趺纯梢蕴^(guò)這一步不判斷直接進(jìn)行我們的轉(zhuǎn)換邏輯呢?

這個(gè)時(shí)候就可以使用 Netty 的

io.netty.handler.codec.ReplayingDecoder

可以不用判斷可讀字節(jié)數(shù)的原理:

ReplayingDecoderByteToMessage 的子類(lèi),源碼如下:

public abstract class ReplayingDecoder<S> extends ByteToMessageDecoder {
    ...
}

ReplayingDecoder 的秘密就是對(duì) ByteToMessageCallDecode(...) 方法的重寫(xiě),觀摩一下具體實(shí)現(xiàn):

@Override
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
    replayable.setCumulation(in);
    try {
        while (in.isReadable()) {
            int oldReaderIndex = checkpoint = in.readerIndex();
            int outSize = out.size();

            if (outSize > 0) {
                fireChannelRead(ctx, out, outSize);
                out.clear();

                // Check if this handler was removed before continuing with decoding.
                // If it was removed, it is not safe to continue to operate on the buffer.
                //
                // See:
                // - https://github.com/netty/netty/issues/4635
                if (ctx.isRemoved()) {
                    break;
                }
                outSize = 0;
            }

            S oldState = state;
            int oldInputLength = in.readableBytes();
            try {
                decodeRemovalReentryProtection(ctx, replayable, out);

                // Check if this handler was removed before continuing the loop.
                // If it was removed, it is not safe to continue to operate on the buffer.
                //
                // See https://github.com/netty/netty/issues/1664
                if (ctx.isRemoved()) {
                    break;
                }

                if (outSize == out.size()) {
                    if (oldInputLength == in.readableBytes() && oldState == state) {
                        throw new DecoderException(
                                StringUtil.simpleClassName(getClass()) + ".decode() must consume the inbound " +
                                "data or change its state if it did not decode anything.");
                    } else {
                        // Previous data has been discarded or caused state transition.
                        // Probably it is reading on.
                        continue;
                    }
                }
            } catch (Signal replay) {
                replay.expect(REPLAY);

                // Check if this handler was removed before continuing the loop.
                // If it was removed, it is not safe to continue to operate on the buffer.
                //
                // See https://github.com/netty/netty/issues/1664
                if (ctx.isRemoved()) {
                    break;
                }

                // Return to the checkpoint (or oldPosition) and retry.
                int checkpoint = this.checkpoint;
                if (checkpoint >= 0) {
                    in.readerIndex(checkpoint);
                } else {
                    // Called by cleanup() - no need to maintain the readerIndex
                    // anymore because the buffer has been released already.
                }
                break;
            }

            if (oldReaderIndex == in.readerIndex() && oldState == state) {
                throw new DecoderException(
                       StringUtil.simpleClassName(getClass()) + ".decode() method must consume the inbound data " +
                       "or change its state if it decoded something.");
            }
            if (isSingleDecode()) {
                break;
            }
        }
    } catch (DecoderException e) {
        throw e;
    } catch (Exception cause) {
        throw new DecoderException(cause);
    }
}

實(shí)現(xiàn)不需要判斷的邏輯就是因?yàn)?/p>

int oldReaderIndex = checkpoint = in.readerIndex(); 

如果在執(zhí)行過(guò)程中出現(xiàn)異常就會(huì)在代碼中重置 index

總結(jié)

雖然 ReplayingDecoder節(jié)約了判斷的邏輯,但是從他的代碼實(shí)現(xiàn)邏輯看到是通過(guò)拋出異常來(lái)不斷的重試,所以在某些特殊的情況下會(huì)造成性能的下降。所以還是再選擇的時(shí)候要根據(jù)自己的實(shí)際需求來(lái)判斷是使用 ByteToMessage 還是使用 ReplayingDecoder

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring實(shí)現(xiàn)內(nèi)置監(jiān)聽(tīng)器

    Spring實(shí)現(xiàn)內(nèi)置監(jiān)聽(tīng)器

    這篇文章主要介紹了Spring 實(shí)現(xiàn)自定義監(jiān)聽(tīng)器案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧,希望能給你帶來(lái)幫助
    2021-07-07
  • Spring Boot + Kotlin + Gradle 構(gòu)建現(xiàn)代化后端應(yīng)用(常見(jiàn)問(wèn)題及方案)

    Spring Boot + Kotlin + Gradle 

    本文將系統(tǒng)梳理使用Spring Boot、Kotlin 和 Gradle(Kotlin DSL) 開(kāi)發(fā)項(xiàng)目的全流程,涵蓋項(xiàng)目初始化、核心開(kāi)發(fā)要點(diǎn)、多模塊架構(gòu)設(shè)計(jì)以及常見(jiàn)問(wèn)題解決方案,幫助開(kāi)發(fā)者快速上手并規(guī)避典型陷阱,感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • Java String.join()從入門(mén)到高階架構(gòu)實(shí)踐

    Java String.join()從入門(mén)到高階架構(gòu)實(shí)踐

    本文詳細(xì)介紹了Java 8中引入的String.join()方法,探討了其設(shè)計(jì)哲學(xué)、核心技術(shù)原理、高級(jí)用法、架構(gòu)模式、性能優(yōu)化以及實(shí)際應(yīng)用場(chǎng)景,String.join()不僅簡(jiǎn)化了字符串拼接操作,還提供了更好的性能和可維護(hù)性,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • springsecurity第三方授權(quán)認(rèn)證的項(xiàng)目實(shí)踐

    springsecurity第三方授權(quán)認(rèn)證的項(xiàng)目實(shí)踐

    Spring security 是一個(gè)強(qiáng)大的和高度可定制的身份驗(yàn)證和訪(fǎng)問(wèn)控制框架,本文主要介紹了springsecurity第三方授權(quán)認(rèn)證的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣可以了解一下
    2023-08-08
  • java中調(diào)用GDAL DLL的實(shí)現(xiàn)方法

    java中調(diào)用GDAL DLL的實(shí)現(xiàn)方法

    本篇文章是對(duì)java中調(diào)用GDAL DLL的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • SpringBoot訪(fǎng)問(wèn)windows共享文件的方法

    SpringBoot訪(fǎng)問(wèn)windows共享文件的方法

    這篇文章主要介紹了SpringBoot訪(fǎng)問(wèn)windows共享文件,項(xiàng)目使用minio存儲(chǔ)且不在同一臺(tái)服務(wù)器上,為了優(yōu)化速度決定使用windows共享功能進(jìn)行文件傳輸,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • Mybatis中的PageHelper的執(zhí)行流程分析

    Mybatis中的PageHelper的執(zhí)行流程分析

    這篇文章主要介紹了Mybatis的PageHelper執(zhí)行流程,本文給大家介紹介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • Java語(yǔ)言實(shí)現(xiàn)對(duì)MySql數(shù)據(jù)庫(kù)中數(shù)據(jù)的增刪改查操作的代碼

    Java語(yǔ)言實(shí)現(xiàn)對(duì)MySql數(shù)據(jù)庫(kù)中數(shù)據(jù)的增刪改查操作的代碼

    這篇文章主要介紹了Java語(yǔ)言實(shí)現(xiàn)對(duì)MySql數(shù)據(jù)庫(kù)中數(shù)據(jù)的增刪改查操作的代碼,實(shí)現(xiàn)了連接數(shù)據(jù)庫(kù),和數(shù)據(jù)庫(kù)的增刪改查操作,有興趣的可以了解一下。
    2016-12-12
  • Java編程之雙重循環(huán)打印圖形

    Java編程之雙重循環(huán)打印圖形

    這篇文章主要介紹了Java編程之雙重循環(huán)打印圖形,屬于Java編程基礎(chǔ)練習(xí)部分,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Java動(dòng)態(tài)導(dǎo)出Word登記表的完整方案

    Java動(dòng)態(tài)導(dǎo)出Word登記表的完整方案

    本文詳細(xì)講解如何使用 Java動(dòng)態(tài)導(dǎo)出包含多人員報(bào)名表的Word文檔,每人占據(jù)獨(dú)立一頁(yè),并支持動(dòng)態(tài)表格行,我們對(duì)比了多種實(shí)現(xiàn)方案,最終推薦基于 Freemarker + XML模板或docx4j的靈活方式,并提供完整代碼示例與最佳實(shí)踐,助你高效實(shí)現(xiàn)復(fù)雜Word導(dǎo)出需求
    2025-07-07

最新評(píng)論

包头市| 南川市| 安西县| 来宾市| 邢台市| 金沙县| 建德市| 安乡县| 左贡县| 遵义县| 甘孜| 富川| 武邑县| 瓦房店市| 荣成市| 巧家县| 得荣县| 攀枝花市| 射洪县| 石棉县| 扬州市| 时尚| 浪卡子县| 新龙县| 驻马店市| 西盟| 韶山市| 杭锦旗| 什邡市| 昆山市| 普宁市| 大丰市| 独山县| 巴彦县| 璧山县| 五常市| 南漳县| 盘山县| 平度市| 思茅市| 黔东|