国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Simple RPC - 02 通用高性能序列化和反序列化設(shè)計與實現(xiàn)

這篇具有很好參考價值的文章主要介紹了Simple RPC - 02 通用高性能序列化和反序列化設(shè)計與實現(xiàn)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

Simple RPC - 02 通用高性能序列化和反序列化設(shè)計與實現(xiàn),【Simple RPC】,rpc,網(wǎng)絡(luò)協(xié)議,序列化和反序列化

概述

網(wǎng)絡(luò)傳輸和序列化這兩部分的功能相對來說是非常通用并且獨(dú)立的,在設(shè)計的時候,只要能做到比較好的抽象,這兩部的實現(xiàn),它的通用性是非常強(qiáng)的。不僅可以用于 RPC 框架中,同樣可以直接拿去用于實現(xiàn)消息隊列,或者其他需要互相通信的分布式系統(tǒng)中。

我們先來實現(xiàn)序列化和反序列化部分,因為后面的部分會用到序列化和反序列化。


設(shè)計實現(xiàn)

通用的序列化接口

首先我們需要設(shè)計一個可擴(kuò)展的,通用的序列化接口,為了方便使用,我們直接使用靜態(tài)類的方式來定義這個接口(嚴(yán)格來說這并不是一個接口)


public class SerializeSupport {
    public static  <E> E parse(byte [] buffer) {
        // ...
    }
    public static <E> byte [] serialize(E  entry) {
        // ...
    }
}
  • parse 方法用于反序列化
  • serialize 方法用于序列化

比如

// 序列化
MyClass myClassObject = new MyClass();
byte [] bytes = SerializeSupport.serialize(myClassObject);
// 反序列化
MyClass myClassObject1 = SerializeSupport.parse(bytes);

通用的序列化實現(xiàn)【推薦】 vs 專用的序列化實現(xiàn)

在講解序列化和反序列化的時候說過,可以使用通用的序列化實現(xiàn),也可以自己來定義專用的序列化實現(xiàn)。

  • 專用的序列化性能最好,但缺點是實現(xiàn)起來比較復(fù)雜,你要為每一種類型的數(shù)據(jù)專門編寫序列化和反序列化方法。
  • 一般的 RPC 框架采用的都是通用的序列化實現(xiàn),比如 gRPC 采用的是 Protobuf 序列化實現(xiàn),Dubbo 支持 hession2 等好幾種序列化實現(xiàn)

為什么這些 RPC 框架不像消息隊列一樣,采用性能更好的專用的序列化實現(xiàn)呢?這個原因很簡單,消息隊列它需要序列化數(shù)據(jù)的類型是固定的,只是它自己的內(nèi)部通信的一些命令。但 RPC 框架,它需要序列化的數(shù)據(jù)是,用戶調(diào)用遠(yuǎn)程方法的參數(shù),這些參數(shù)可能是各種數(shù)據(jù)類型,所以必須使用通用的序列化實現(xiàn),確保各種類型的數(shù)據(jù)都能被正確的序列化和反序列化。


我們這里還是采用專用的序列化實現(xiàn),主要的目的是一起來實踐一下,如何來實現(xiàn)序列化和反序列化

專用序列化接口定義

public interface Serializer<T> {
    /**
     * 計算對象序列化后的長度,主要用于申請存放序列化數(shù)據(jù)的字節(jié)數(shù)組
     * @param entry 待序列化的對象
     * @return 對象序列化后的長度
     */
    int size(T entry);
    /**
     * 序列化對象。將給定的對象序列化成字節(jié)數(shù)組
     * @param entry 待序列化的對象
     * @param bytes 存放序列化數(shù)據(jù)的字節(jié)數(shù)組
     * @param offset 數(shù)組的偏移量,從這個位置開始寫入序列化數(shù)據(jù)
     * @param length 對象序列化后的長度,也就是{@link Serializer#size(java.lang.Object)}方法的返回值。
     */
    void serialize(T entry, byte[] bytes, int offset, int length);
    /**
     * 反序列化對象
     * @param bytes 存放序列化數(shù)據(jù)的字節(jié)數(shù)組
     * @param offset 數(shù)組的偏移量,從這個位置開始寫入序列化數(shù)據(jù)
     * @param length 對象序列化后的長度
     * @return 反序列化之后生成的對象
     */
    T parse(byte[] bytes, int offset, int length);
    /**
     * 用一個字節(jié)標(biāo)識對象類型,每種類型的數(shù)據(jù)應(yīng)該具有不同的類型值
     */
    byte type();
    /**
     * 返回序列化對象類型的Class對象。
     */
    Class<T> getSerializeClass();
}

這個接口中,除了 serialize 和 parse 這兩個序列化和反序列化兩個方法以外,還定義了下面這幾個方法:

  • size 方法計算序列化之后的數(shù)據(jù)長度,用于事先來申請存放序列化數(shù)據(jù)的字節(jié)數(shù)組;
  • type 方法定義每種序列化實現(xiàn)的類型,這個類型值也會寫入到序列化之后的數(shù)據(jù)中,主要的作用是在反序列化的時候,能夠識別是什么數(shù)據(jù)類型的,以便找到對應(yīng)的反序列化實現(xiàn)類;
  • getSerializeClass 這個方法返回這個序列化實現(xiàn)類對應(yīng)的對象類型,目的是,在執(zhí)行序列化的時候,通過被序列化的對象類型找到對應(yīng)序列化實現(xiàn)類

序列化實現(xiàn)

利用這個 Serializer 接口,我們就可以來實現(xiàn) SerializeSupport 這個支持任何對象類型序列化的通用靜態(tài)類了。

首先我們定義兩個 Map,這兩個 Map 中存放著所有實現(xiàn) Serializer 接口的序列化實現(xiàn)類

private static Map<Class<?>/*序列化對象類型*/, Serializer<?>/*序列化實現(xiàn)*/> serializerMap = new HashMap<>();
private static Map<Byte/*序列化實現(xiàn)類型*/, Class<?>/*序列化對象類型*/> typeMap = new HashMap<>();

  • serializerMap 中的 key 是序列化實現(xiàn)類對應(yīng)的序列化對象的類型,它的用途是在序列化的時候,通過被序列化的對象類型,找到對應(yīng)的序列化實現(xiàn)類。
  • typeMap 的作用和 serializerMap 是類似的,它的 key 是序列化實現(xiàn)類的類型,用于在反序列化的時候,從序列化的數(shù)據(jù)中讀出對象類型,然后找到對應(yīng)的序列化實現(xiàn)類

理解了這兩個 Map 的作用,實現(xiàn)序列化和反序列化這兩個方法就很容易了。這兩個方法的實現(xiàn)思路是一樣的,都是通過一個類型在這兩個 Map 中進(jìn)行查找,查找的結(jié)果就是對應(yīng)的序列化實現(xiàn)類的實例,也就是 Serializer 接口的實現(xiàn),然后調(diào)用對應(yīng)的序列化或者反序列化方法就可以了。

public class SerializeSupport {
    private static final Logger logger = LoggerFactory.getLogger(SerializeSupport.class);
    private static Map<Class<?>/*序列化對象類型*/, Serializer<?>/*序列化實現(xiàn)*/> serializerMap = new HashMap<>();
    private static Map<Byte/*序列化實現(xiàn)類型*/, Class<?>/*序列化對象類型*/> typeMap = new HashMap<>();

    static {
        for (Serializer serializer : ServiceSupport.loadAll(Serializer.class)) {
            registerType(serializer.type(), serializer.getSerializeClass(), serializer);
            logger.info("Found serializer, class: {}, type: {}.",
                    serializer.getSerializeClass().getCanonicalName(),
                    serializer.type());
        }
    }
    private static byte parseEntryType(byte[] buffer) {
        return buffer[0];
    }
    private static <E> void registerType(byte type, Class<E> eClass, Serializer<E> serializer) {
        serializerMap.put(eClass, serializer);
        typeMap.put(type, eClass);
    }
    @SuppressWarnings("unchecked")
    private static  <E> E parse(byte [] buffer, int offset, int length, Class<E> eClass) {
        Object entry =  serializerMap.get(eClass).parse(buffer, offset, length);
        if (eClass.isAssignableFrom(entry.getClass())) {
            return (E) entry;
        } else {
            throw new SerializeException("Type mismatch!");
        }
    }
    public static  <E> E parse(byte [] buffer) {
        return parse(buffer, 0, buffer.length);
    }

    private static  <E> E parse(byte[] buffer, int offset, int length) {
        byte type = parseEntryType(buffer);
        @SuppressWarnings("unchecked")
        Class<E> eClass = (Class<E> )typeMap.get(type);
        if(null == eClass) {
            throw new SerializeException(String.format("Unknown entry type: %d!", type));
        } else {
            return parse(buffer, offset + 1, length - 1,eClass);
        }

    }

    public static <E> byte [] serialize(E  entry) {
        @SuppressWarnings("unchecked")
        Serializer<E> serializer = (Serializer<E>) serializerMap.get(entry.getClass());
        if(serializer == null) {
            throw new SerializeException(String.format("Unknown entry class type: %s", entry.getClass().toString()));
        }
        byte [] bytes = new byte [serializer.size(entry) + 1];
        bytes[0] = serializer.type();
        serializer.serialize(entry, bytes, 1, bytes.length - 1);
        return bytes;
    }
}

所有的 Serializer 的實現(xiàn)類是怎么加載到 SerializeSupport 的那兩個 Map 中的呢?這里面利用了 Java 的一個 SPI 類加載機(jī)制

public class ServiceSupport {
    private final static Map<String, Object> singletonServices = new HashMap<>();
    public synchronized static <S> S load(Class<S> service) {
        return StreamSupport.
                stream(ServiceLoader.load(service).spliterator(), false)
                .map(ServiceSupport::singletonFilter)
                .findFirst().orElseThrow(ServiceLoadException::new);
    }
    public synchronized static <S> Collection<S> loadAll(Class<S> service) {
        return StreamSupport.
                stream(ServiceLoader.load(service).spliterator(), false)
                .map(ServiceSupport::singletonFilter).collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    private static <S>  S singletonFilter(S service) {

        if(service.getClass().isAnnotationPresent(Singleton.class)) {
            String className = service.getClass().getCanonicalName();
            Object singletonInstance = singletonServices.putIfAbsent(className, service);
            return singletonInstance == null ? service : (S) singletonInstance;
        } else {
            return service;
        }
    }
}

到這里,我們就封裝好了一個通用的序列化的接口,

  • 對于使用序列化的模塊來說,它只要依賴 SerializeSupport 這個靜態(tài)類,調(diào)用它的序列化和反序列化方法就可以了,不需要依賴任何序列化實現(xiàn)類。

  • 對于序列化實現(xiàn)的提供者來說,也只需要依賴并實現(xiàn) Serializer 這個接口就可以了。

比如,我們的 HelloService 例子中的參數(shù)是一個 String 類型的數(shù)據(jù),我們需要實現(xiàn)一個支持 String 類型的序列化實現(xiàn)

public class StringSerializer implements Serializer<String> {
    @Override
    public int size(String entry) {
        return entry.getBytes(StandardCharsets.UTF_8).length;
    }
    @Override
    public void serialize(String entry, byte[] bytes, int offset, int length) {
        byte [] strBytes = entry.getBytes(StandardCharsets.UTF_8);
        System.arraycopy(strBytes, 0, bytes, offset, strBytes.length);
    }
    @Override
    public String parse(byte[] bytes, int offset, int length) {
        return new String(bytes, offset, length, StandardCharsets.UTF_8);
    }
    @Override
    public byte type() {
        return Types.TYPE_STRING;
    }
    @Override
    public Class<String> getSerializeClass() {
        return String.class;
    }
}

在把 String 和 byte 數(shù)組做轉(zhuǎn)換的時候,一定要指定編碼方式,確保序列化和反序列化的時候都使用一致的編碼,我們這里面統(tǒng)一使用 UTF8 編碼。否則,如果遇到執(zhí)行序列化和反序列化的兩臺服務(wù)器默認(rèn)編碼不一樣,就會出現(xiàn)亂碼。我們在開發(fā)過程用遇到的很多中文亂碼問題,絕大部分都是這個原因

還有一個更復(fù)雜的序列化實現(xiàn) MetadataSerializer,用于將注冊中心的數(shù)據(jù)持久化到文件中

/**
 * Size of the map                     2 bytes
 *      Map entry:
 *          Key string:
 *              Length:                2 bytes
 *              Serialized key bytes:  variable length
 *          Value list
 *              List size:              2 bytes
 *              item(URI):
 *                  Length:             2 bytes
 *                  serialized uri:     variable length
 *              item(URI):
 *              ...
 *      Map entry:
 *      ...
 *
 */
public class MetadataSerializer implements Serializer<Metadata> {

    @Override
    public int size(Metadata entry) {
        return Short.BYTES +                   // Size of the map                  2 bytes
                entry.entrySet().stream()
                        .mapToInt(this::entrySize).sum();
    }

    @Override
    public void serialize(Metadata entry, byte[] bytes, int offset, int length) {

        ByteBuffer buffer = ByteBuffer.wrap(bytes, offset, length);
        buffer.putShort(toShortSafely(entry.size()));

        entry.forEach((k,v) -> {
            byte [] keyBytes = k.getBytes(StandardCharsets.UTF_8);
            buffer.putShort(toShortSafely(keyBytes.length));
            buffer.put(keyBytes);

            buffer.putShort(toShortSafely(v.size()));
            for (URI uri : v) {
                byte [] uriBytes = uri.toASCIIString().getBytes(StandardCharsets.UTF_8);
                buffer.putShort(toShortSafely(uriBytes.length));
                buffer.put(uriBytes);
            }

        });
    }

    private int entrySize(Map.Entry<String, List<URI>> e) {
        // Map entry:
        return Short.BYTES +       // Key string length:               2 bytes
                e.getKey().getBytes().length +    // Serialized key bytes:   variable length
                Short.BYTES + // List size:              2 bytes
                e.getValue().stream() // Value list
                        .mapToInt(uri -> {
                            return Short.BYTES +       // Key string length:               2 bytes
                                    uri.toASCIIString().getBytes(StandardCharsets.UTF_8).length;    // Serialized key bytes:   variable length
                        }).sum();
    }

    @Override
    public Metadata parse(byte[] bytes, int offset, int length) {
        ByteBuffer buffer = ByteBuffer.wrap(bytes, offset, length);

        Metadata metadata = new Metadata();
        int sizeOfMap = buffer.getShort();
        for (int i = 0; i < sizeOfMap; i++) {
            int keyLength = buffer.getShort();
            byte [] keyBytes = new byte [keyLength];
            buffer.get(keyBytes);
            String key = new String(keyBytes, StandardCharsets.UTF_8);


            int uriListSize = buffer.getShort();
            List<URI> uriList = new ArrayList<>(uriListSize);
            for (int j = 0; j < uriListSize; j++) {
                int uriLength = buffer.getShort();
                byte [] uriBytes = new byte [uriLength];
                buffer.get(uriBytes);
                URI uri  = URI.create(new String(uriBytes, StandardCharsets.UTF_8));
                uriList.add(uri);
            }
            metadata.put(key, uriList);
        }
        return metadata;
    }

    @Override
    public byte type() {
        return Types.TYPE_METADATA;
    }

    @Override
    public Class<Metadata> getSerializeClass() {
        return Metadata.class;
    }

    private short toShortSafely(int v) {
        assert v < Short.MAX_VALUE;
        return (short) v;
    }
}

到這里序列化的部分就實現(xiàn)完成了。我們這個序列化的實現(xiàn),對外提供服務(wù)的就只有一個 SerializeSupport 靜態(tài)類,并且可以通過擴(kuò)展支持序列化任何類型的數(shù)據(jù),這樣一個通用的實現(xiàn),不僅可以用在我們這個 RPC 框架的例子中,完全可以把這部分直接拿過去用在業(yè)務(wù)代碼中文章來源地址http://www.zghlxwxcb.cn/news/detail-721287.html


到了這里,關(guān)于Simple RPC - 02 通用高性能序列化和反序列化設(shè)計與實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 高性能 RPC 框架 CloudWeGo-Kitex 內(nèi)外統(tǒng)一的開源實踐

    高性能 RPC 框架 CloudWeGo-Kitex 內(nèi)外統(tǒng)一的開源實踐

    日前,字節(jié)跳動技術(shù)社區(qū) ByteTech 舉辦的第七期字節(jié)跳動技術(shù)沙龍圓滿落幕,本期沙龍以《字節(jié)高性能開源微服務(wù)框架:CloudWeGo》為主題。在沙龍中,字節(jié)跳動字節(jié)跳動基礎(chǔ)架構(gòu)服務(wù)框架資深研發(fā)工程師 楊芮 ,跟大家分享了《高性能 RPC 框架 Kitex 內(nèi)外統(tǒng)一的開源實踐》,本文

    2024年02月03日
    瀏覽(34)
  • 《Linux高性能服務(wù)器編程》筆記02

    《Linux高性能服務(wù)器編程》筆記02

    參考 Linux高性能服務(wù)器編程源碼: https://github.com/raichen/LinuxServerCodes 豆瓣: Linux高性能服務(wù)器編程 Linux提供了很多高級的I/O函數(shù)。它們并不像Linux基礎(chǔ)I/O函數(shù)(比如open和read) 那么常用(編寫內(nèi)核模塊時一般要實現(xiàn)這些I/O函數(shù)),但在特定的條件下卻表現(xiàn)出優(yōu)秀的性 能。本章將討論

    2024年01月21日
    瀏覽(30)
  • 【RocketMq系列-02】RocketMq的架構(gòu)解析和高性能設(shè)計

    【RocketMq系列-02】RocketMq的架構(gòu)解析和高性能設(shè)計

    RocketMq系列整體欄目 內(nèi)容 鏈接地址 【一】RocketMq安裝和基本概念 https://zhenghuisheng.blog.csdn.net/article/details/134486709 【二】RocketMq的架構(gòu)解析和高性能設(shè)計/font https://zhenghuisheng.blog.csdn.net/article/details/134559514 在rocketMq中,其整體架構(gòu)如下,在RocketMqServer中,主要有NameServer,Broker,

    2024年02月05日
    瀏覽(25)
  • 讀高性能MySQL(第4版)筆記02_MySQL架構(gòu)(下)

    讀高性能MySQL(第4版)筆記02_MySQL架構(gòu)(下)

    2.6.4.1.?失敗的事務(wù)可能導(dǎo)致不一致的結(jié)果,因為某些部分可以回滾,而其他部分不能回滾 5.1.1.1.?在表的.ibd文件中 5.1.1.2.?減少了I/O,非常高效 5.2.1.1.?分區(qū)定義 5.2.1.2.?表定義 5.2.1.3.?存儲程序定義 5.2.1.4.?字符集 5.2.1.5.?排序信息 5.2.2.1.?每個表的.ibd和.frm文件被替換為已經(jīng)

    2024年02月12日
    瀏覽(19)
  • 《高性能MySQL》——創(chuàng)建高性能的索引(筆記)

    《高性能MySQL》——創(chuàng)建高性能的索引(筆記)

    索引(在MySQL中也叫做“鍵(key)”) 是存儲引擎用于快速找到記錄的一種數(shù)據(jù)結(jié)構(gòu)。 索引對于良好的性能非常關(guān)鍵。尤其是當(dāng)表中的數(shù)據(jù)量越來越大時,索引對性能的影響愈發(fā)重要。 在數(shù)據(jù)量較小且負(fù)載較低時,不恰當(dāng)?shù)乃饕龑π阅艿挠绊懣赡苓€不明顯,但當(dāng)數(shù)據(jù)量逐漸增大時

    2024年02月07日
    瀏覽(99)
  • 【Linux高性能服務(wù)器編程】——高性能服務(wù)器框架

    【Linux高性能服務(wù)器編程】——高性能服務(wù)器框架

    ? hello !大家好呀! 歡迎大家來到我的Linux高性能服務(wù)器編程系列之高性能服務(wù)器框架介紹,在這篇文章中, 你將會學(xué)習(xí)到高效的創(chuàng)建自己的高性能服務(wù)器,并且我會給出源碼進(jìn)行剖析,以及手繪UML圖來幫助大家來理解,希望能讓大家更能了解網(wǎng)絡(luò)編程技術(shù)?。?! 希望這篇

    2024年04月25日
    瀏覽(101)
  • 讀高性能MySQL(第4版)筆記08_創(chuàng)建高性能索引(上)

    讀高性能MySQL(第4版)筆記08_創(chuàng)建高性能索引(上)

    2.4.2.1.?按照索引列中的數(shù)據(jù)大小順序存儲的 2.4.3.1.?鍵前綴查找只適用于根據(jù)最左前綴的查找 2.4.4.1.?在查詢某些條件的數(shù)據(jù)時,存儲引擎不再需要進(jìn)行全表掃描 2.4.4.2.?通過比較節(jié)點頁的值和要查找的值可以找到合適的指針進(jìn)入下層子節(jié)點,這些指針實際上定義了子節(jié)點頁中

    2024年02月08日
    瀏覽(98)
  • 讀高性能MySQL(第4版)筆記09_創(chuàng)建高性能索引(下)

    讀高性能MySQL(第4版)筆記09_創(chuàng)建高性能索引(下)

    1.4.4.1.?InnoDB的二級索引在葉子節(jié)點中保存了記錄的主鍵值,所以如果二級索引能夠覆蓋查詢,則可以避免對主鍵索引的二次查詢 7.1.5.1.?常見的類似錯誤通常是由于嘗試使用rsync備份InnoDB導(dǎo)致的 7.3.3.1.?否則,對于范圍查詢、索引覆蓋掃描等操作來說,速度可能會降低很多 7

    2024年02月08日
    瀏覽(103)
  • 《高性能MYSQL》-- 查詢性能優(yōu)化

    《高性能MYSQL》-- 查詢性能優(yōu)化

    查詢性能優(yōu)化 深刻地理解MySQL如何真正地執(zhí)行查詢,并明白高效和低效的原因何在 查詢的生命周期(不完整):從客戶端到服務(wù)器,然后服務(wù)器上進(jìn)行語法解析,生成執(zhí)行計劃,執(zhí)行,并給客戶端返回結(jié)果。 一條查詢,如果查詢得很慢,原因大概率是訪問的數(shù)據(jù)太多 對于低

    2024年03月11日
    瀏覽(99)
  • 高性能MySQL實戰(zhàn)(三):性能優(yōu)化

    大家好,我是 方圓 。這篇主要介紹對慢 SQL 優(yōu)化的一些手段,而在講解具體的優(yōu)化措施之前,我想先對 EXPLAIN 進(jìn)行介紹,它是我們在分析查詢時必要的操作,理解了它輸出結(jié)果的內(nèi)容更有利于我們優(yōu)化 SQL。為了方便大家的閱讀,在下文中規(guī)定類似 key1 的表示二級索引,key_

    2024年02月11日
    瀏覽(97)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包