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

Netty中序列化的作用及自定義協(xié)議詳解

 更新時間:2023年12月21日 10:10:31   作者:Colins~  
這篇文章主要介紹了Netty中序列化的作用及自定義協(xié)議詳解,Netty自身就支持很多種協(xié)議比如Http、Websocket等等,但如果用來作為自己的RPC框架通常會自定義協(xié)議,所以這也是本文的重點,需要的朋友可以參考下

前言

上一章已經(jīng)說了怎么解決沾包和拆包的問題,但是這樣離一個成熟的通信還是有一點距離,我們還需要讓服務(wù)端和客戶端使用同一個"語言"來溝通,要不然一個講英文一個講中文,兩個都聽不懂豈不是很尷尬?這種語言就叫協(xié)議。

Netty自身就支持很多種協(xié)議比如Http、Websocket等等,但如果用來作為自己的RPC框架通常會自定義協(xié)議,所以這也是本文的重點!

序列化的重要性

在說協(xié)議之前,我們需要先知道什么是序列化,序列化是干嘛的?

我們要知道數(shù)據(jù)在傳輸?shù)倪^程中是以0和1的形式傳輸?shù)?,而把對象轉(zhuǎn)化成二進(jìn)制的過程就叫序列化,將二進(jìn)制轉(zhuǎn)化為對象的過程就叫反序列化。

為什么要說這個很重要呢?因為序列化和反序列化是需要耗時的,而序列化后的字節(jié)大小也會影響到傳輸?shù)男?,所以選對一種高效的序列化方式是非常之重要的,下面我們以JDK自帶的序列化和我們常用的JSON序列化來做一個對比,序列化后大小的對比、序列化效率的對比

大小對比

我們先準(zhǔn)備一個實體類SerializeTestVO實現(xiàn)Serializable 接口

public class SerializeTestVO implements Serializable {
    private Integer id;
    private String name;
    private Integer age;
    private Integer sex;
    private Integer bodyWeight;
    private Integer height;
    private String school;
    //Set、get方法省略
}

測試方法:

public static void main(String[] args) throws IOException {
        // 普普通通的實體類
        SerializeTestVO serializeTestVO = new SerializeTestVO();
        serializeTestVO.setAge(18);
        serializeTestVO.setBodyWeight(120);
        serializeTestVO.setHeight(180);
        serializeTestVO.setId(10000);
        serializeTestVO.setName("張三");
        serializeTestVO.setSchool("XXXXXXXXXXXX");
        // JDK序列化
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(serializeTestVO);
        objectOutputStream.flush();
        objectOutputStream.close();
        System.out.println("JDK 序列化大小: "+(byteArrayOutputStream.toByteArray().length));
        byteArrayOutputStream.close();
        //JSON序列化
        System.out.println("JSON 序列化大小: " + JSON.toJSONString(serializeTestVO).getBytes().length);
}

結(jié)果:

在這里插入圖片描述

可以看到序列化后大小相差了好幾倍,這也意味著傳輸效率的幾倍

效率對比

實體類保持不變,我們序列化300W次,看看結(jié)果

public static void main(String[] args) throws IOException {
        SerializeTestVO serializeTestVO = new SerializeTestVO();
        serializeTestVO.setAge(18);
        serializeTestVO.setBodyWeight(120);
        serializeTestVO.setHeight(180);
        serializeTestVO.setId(10000);
        serializeTestVO.setName("張三");
        serializeTestVO.setSchool("XXXXXXXXXXXX");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 3000000; i++) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(serializeTestVO);
            objectOutputStream.flush();
            objectOutputStream.close();
            byte[] bytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.close();
        }
        System.out.println("JDK 序列化耗時: " + (System.currentTimeMillis() - start));
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < 3000000; i++) {
            byte[] bytes = JSON.toJSONString(serializeTestVO).getBytes();
        }
        System.out.println("JSON 序列化耗時: " + (System.currentTimeMillis() - start1));
    }

結(jié)果:

在這里插入圖片描述

幾乎6倍的差距,結(jié)合序列化后的大小綜合來看,選擇一種好的序列化方式是多么的重要

自定義協(xié)議

其實到現(xiàn)在我們已經(jīng)掌握了自定義協(xié)議里面最關(guān)鍵的幾個點了,序列化、數(shù)據(jù)結(jié)構(gòu)、編解碼器,我們一個一個來

序列化

直接采用我們常用且熟悉的JSON序列化

數(shù)據(jù)結(jié)構(gòu)

我們設(shè)置為消息頭和消息體,結(jié)構(gòu)如下:

在這里插入圖片描述

消息頭包含:開始標(biāo)志、時間戳、消息體長度

消息體包含:通信憑證、消息ID、消息類型、消息

實體類如下

@Data
public class NettyMsg {
    private NettyMsgHead msgHead=new NettyMsgHead();
    private NettyBody nettyBody;
    public NettyMsg(ServiceCodeEnum codeEnum, Object msg){
        this.nettyBody=new NettyBody(codeEnum, msg);
    }
}
@Data
public class NettyMsgHead {
    // 開始標(biāo)識
    private short startSign = (short) 0xFFFF;
    // 時間戳
    private final int timeStamp;
    public NettyMsgHead(){
        this.timeStamp=(int)(DateUtil.current() / 1000);
    }
}
@Data
public class NettyBody {
    // 通信憑證
    private String token;
    // 消息ID
    private String msgId;
    // 消息類型
    private short msgType;
    // 消息 這里序列化采用JSON序列化
    // 所以這個msg可以是實體類的msg 兩端通過消息類型來判斷實體類類型
    private String msg;
    public NettyBody(){
    }
    public NettyBody(ServiceCodeEnum codeEnum,Object msg){
        this.token=""; // 鑒權(quán)使用
        this.msgId=""; // 拓展使用
        this.msgType=codeEnum.getCode();
        this.msg= JSON.toJSONString(msg);
    }
}

消息類型枚舉

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ServiceCodeEnum {
    TEST_TYPE((short) 0xFFF1, "測試");


    private final short code;
    private final String desc;

    ServiceCodeEnum(short code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    
    public short getCode() {
        return code;
    }

}

自定義編碼器

編碼器的作用就是固定好我們的數(shù)據(jù)格式,無需在每次發(fā)送數(shù)據(jù)的時候還需要去對數(shù)據(jù)進(jìn)行格式編碼

public class MyNettyEncoder extends MessageToByteEncoder<NettyMsg> {
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, NettyMsg msg, ByteBuf out) throws Exception {
        // 寫入開頭的標(biāo)志
        out.writeShort(msg.getMsgHead().getStartSign());
        // 寫入秒時間戳
        out.writeInt(msg.getMsgHead().getTimeStamp());
        byte[] bytes = JSON.toJSON(msg.getNettyBody()).toString().getBytes();
        // 寫入消息長度
        out.writeInt(bytes.length);
        // 寫入消息主體
        out.writeBytes(bytes);
    }
}

自定義解碼器

解碼器的第一個作用就是解決沾包和拆包的問題,第二個作用就是對數(shù)據(jù)有效性的校驗,比如數(shù)據(jù)協(xié)議是否匹配、數(shù)據(jù)是否被篡改、數(shù)據(jù)加解密等等

所以我們直接繼承LengthFieldBasedFrameDecoder類,重寫decode方法,利用父類來解決沾包和拆包問題,自定義來解決數(shù)據(jù)有效性問題

public class MyNettyDecoder extends LengthFieldBasedFrameDecoder {
    // 開始標(biāo)記
    private final short HEAD_START = (short) 0xFFFF;
    public MyNettyDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
    }
    public MyNettyDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
    }
    public MyNettyDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
    }
    public MyNettyDecoder(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
        super(byteOrder, maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
    }
    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        // 經(jīng)過父解碼器的處理 我們就不需要在考慮沾包和半包了
        // 當(dāng)然,想要自己處理沾包和半包問題也不是不可以
        ByteBuf decode = (ByteBuf) super.decode(ctx, in);
        if (decode == null) {
            return null;
        }
        // 開始標(biāo)志校驗  開始標(biāo)志不匹配直接 過濾此條消息
        short startIndex = decode.readShort();
        if (startIndex != HEAD_START) {
            return null;
        }
        // 時間戳
        int timeIndex = decode.readInt();
        // 消息體長度
        int lenOfBody = decode.readInt();
        // 讀取消息
        byte[] msgByte = new byte[lenOfBody];
        decode.readBytes(msgByte);
        String msgContent = new String(msgByte);
        // 將消息轉(zhuǎn)成實體類 傳遞給下面的數(shù)據(jù)處理器
        return JSON.parseObject(msgContent, NettyBody.class);
    }
}

安全性

上述的協(xié)議里面,我只預(yù)留了三種簡單的校驗,一個是開始標(biāo)識,二是消息憑證,三是時間戳,實時上這太簡單了,下面我說幾種可以加上去拓展的:

消息整體加密:消息頭添加一個加密類型,客戶端和服務(wù)端都內(nèi)置幾種加解密手段,在發(fā)送消息的時候隨機(jī)一種加密方式對加密類型、消息長度以外的其他內(nèi)容加密,接收的時候再解密,但是要注意加密后不能影響沾包和拆包的處理

消息體加密:添加結(jié)束標(biāo)識放入消息體,和上述方式類似,但是是對消息體中的內(nèi)容再次加密,可和上述方式結(jié)合,形成二次加密

時間戳:可以對長時間才接收到的消息拒收,或者要求重發(fā)根據(jù)消息ID

加簽和驗簽:對具體的消息加簽和驗簽,防止篡改

憑證:這個很熟悉了,就比如登錄憑證

復(fù)雜格式:上述的數(shù)據(jù)格式還是過于簡單,實際可以整了更加復(fù)雜

驗證

主體代碼呢還是之前的,我們改動幾個地方

NettyClient

解碼器是繼承的LengthFieldBasedFrameDecoder,所以參數(shù)也一樣,不懂的看一下上一篇

在這里插入圖片描述

NettyServer

在這里插入圖片描述

NettyClientTestHandler

發(fā)送100次是為了驗證沾包和拆包,發(fā)送不同的開始標(biāo)志,是為了驗證接收的時候是否有過濾無效數(shù)據(jù)

在這里插入圖片描述

NettyServerTestHandler

有了編碼器,發(fā)送可以直接發(fā)送實體類,有了解碼器我們可以直接用實體類接收數(shù)據(jù),因為解碼器里面往下傳遞的是過濾了消息頭的實體類

在這里插入圖片描述

結(jié)果

一共接收到了50條消息,而且都是偶數(shù)消息,說明無效消息被過濾了,也沒有沾包和拆包

在這里插入圖片描述

到此這篇關(guān)于Netty中序列化的作用及自定義協(xié)議詳解的文章就介紹到這了,更多相關(guān)Netty序列化及自定義協(xié)議內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java面向?qū)ο笾^承、構(gòu)造方法、重寫、重載

    Java面向?qū)ο笾^承、構(gòu)造方法、重寫、重載

    本章具體介紹了什么是構(gòu)造方法、繼承、重寫、重載以及創(chuàng)建方法,整篇文章用老司機(jī)和人類來舉例,圖解穿插代碼案例,需要的朋友可以參考下
    2023-03-03
  • Java SPI簡單應(yīng)用案例詳解

    Java SPI簡單應(yīng)用案例詳解

    這篇文章主要介紹了Java SPI簡單應(yīng)用案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • SpringBoot整合WebSocket實現(xiàn)實時通信功能

    SpringBoot整合WebSocket實現(xiàn)實時通信功能

    在當(dāng)今互聯(lián)網(wǎng)時代,實時通信已經(jīng)成為了許多應(yīng)用程序的基本需求,而WebSocket作為一種全雙工通信協(xié)議,為開發(fā)者提供了一種簡單、高效的實時通信解決方案,本文將介紹如何使用SpringBoot框架來實現(xiàn)WebSocket的集成,快速搭建實時通信功能,感興趣的朋友可以參考下
    2023-11-11
  • Maven 主模塊和子模塊pom.xml依賴聲明

    Maven 主模塊和子模塊pom.xml依賴聲明

    這篇文章主要介紹了Maven 主模塊和子模塊pom.xml依賴聲明,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Spring?IOC容器Bean管理XML注入集合類型屬性

    Spring?IOC容器Bean管理XML注入集合類型屬性

    這篇文章主要為大家介紹了Spring?IOC容器Bean管理XML注入集合類型屬性,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • Spring BPP中如何優(yōu)雅的創(chuàng)建動態(tài)代理Bean詳解

    Spring BPP中如何優(yōu)雅的創(chuàng)建動態(tài)代理Bean詳解

    這篇文章主要給大家介紹了關(guān)于Spring BPP中如何優(yōu)雅的創(chuàng)建動態(tài)代理Bean的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Java實現(xiàn)導(dǎo)出數(shù)據(jù)到csv文件的示例代碼

    Java實現(xiàn)導(dǎo)出數(shù)據(jù)到csv文件的示例代碼

    項目中很多情況會遇到需要導(dǎo)出數(shù)據(jù)到csv文件,這篇文章主要為大家介紹了如何利用Java實現(xiàn)將數(shù)據(jù)導(dǎo)出到csv文件中,感興趣的小伙伴可以了解下
    2025-05-05
  • Java模擬實現(xiàn)機(jī)場過安檢處理流程

    Java模擬實現(xiàn)機(jī)場過安檢處理流程

    這篇文章主要為大家詳細(xì)介紹了用Java模擬實現(xiàn)機(jī)場安全檢查流程的案例,涉及線程的相關(guān)知識,由子線程不斷的檢查通過旅客,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Java 邏輯運(yùn)算符中&&與&,||與|的區(qū)別

    Java 邏輯運(yùn)算符中&&與&,||與|的區(qū)別

    這篇文章主要介紹了Java中&&與&,||與|的區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Java面向?qū)ο笕馕鲋畉his關(guān)鍵字+構(gòu)造方法+標(biāo)準(zhǔn)JavaBean

    Java面向?qū)ο笕馕鲋畉his關(guān)鍵字+構(gòu)造方法+標(biāo)準(zhǔn)JavaBean

    本文介紹了Java面向?qū)ο蟮母呒墐?nèi)容,包括無參構(gòu)造方法和帶參構(gòu)造方法,標(biāo)準(zhǔn)JavaBean類是企業(yè)開發(fā)中用于封裝數(shù)據(jù)的規(guī)范類,必須遵循特定的命名和方法規(guī)范,通過本文學(xué)習(xí),讀者可以掌握這些關(guān)鍵概念并應(yīng)用到實際開發(fā)中,感興趣的朋友跟隨小編一起看看吧
    2026-01-01

最新評論

太谷县| 鸡西市| 忻城县| 易门县| 宁夏| 城步| 隆昌县| 辽阳县| 娄烦县| 怀仁县| 乳山市| 汉中市| 金湖县| 淮安市| 子长县| 灯塔市| 岑溪市| 莱州市| 黑龙江省| 大田县| 镶黄旗| 菏泽市| 出国| 屏东县| 林周县| 泸西县| 静海县| 芒康县| 武宁县| 瑞昌市| 保山市| 镇雄县| 桐庐县| 江华| 乌兰浩特市| 肃宁县| 峨眉山市| 东乡县| 温泉县| 阿克陶县| 盐城市|