歡迎光臨
每天分享高質量文章

【剖析 | SOFARPC 框架】系列之SOFARPC 序列化比較

SOFA

Scalable Open Financial Architecture

是螞蟻金服自主研發的金融級分佈式中間件,包含了構建金融級雲原生架構所需的各個組件,是在金融場景里錘煉出來的最佳實踐。


本文為《剖析 | SOFARPC 框架》最後一篇,作者明不二,就職於華為

《剖析 | SOFARPC 框架》系列由 SOFA 團隊和原始碼愛好者們出品,

專案代號:,官方目錄目前已經全部完成,感謝所有參與的原始碼愛好者!


  前言

在應用服務化架構中,RPC 框架是非常重要的基礎組件。而在 RPC 框架當中,序列化(以及反序列化)又是必不可少的一環。因為序列化的性能對整體框架性能有比較大的影響,之前的文章中,我們已經詳細剖析了 SOFARPC 各個核心功能模塊的實現原理,想必大家已經很清楚 RPC 的呼叫流程。

在整個 RPC 呼叫流程當中,序列化及反序列化起到了承上啟下的作用。序列化時,RPC客戶端把待呼叫的方法和引數物件轉換為網絡上可傳輸的位元組序列,為進一步的編解碼提供原料。反序列化時,把從網絡上接收到且已經解碼了的位元組序列轉換成物件,便於 RPC 服務端呼叫。

本文將從序列化概述、序列化協議特性、序列化使用方法分類、SOFARPC 序列化的設計及實現、幾種序列化協議對比等方面介紹及對比序列化及其在 SOFARPC 中的應用。

  序列化概述

RPC 呼叫通過網絡傳輸相關的呼叫方法及引數,在這個網絡傳輸過程中,記憶體中的物件是無法直接傳輸的,只有二進制位元組才能在網絡上傳輸。而為了實現呼叫物件在網絡上的傳輸,必須通過序列化實現物件 -> 位元組的過程,以及反序列化實現位元組 -> 物件的過程。在網絡協議模型中,序列化屬於應用層協議的一部分。

如下列定義:

序列化:將資料結構或者物件轉換成二進制串的過程。

反序列化:將序列化過程中生成的二進制串轉換成資料結構或者物件的過程。

在上述定義中,二進制位元組陣列專指 Java 語言中的 byte[]

  序列化協議特性

每種序列化協議都有其優點和缺點,在對一個序列化協議進行衡量評判時,通常由如下一些指標可以參考:

指標

說明

重要性

通用性

是否跨平臺,社區如何

中高

可讀

序列化格式是否可讀

中低

易用性

是否簡單易用

中高

性能

序列化後的大小和壓縮 CPU消耗

中高

可擴展性

是在允許欄位修改

安全性

是否存在一些無法修複的漏洞

以下逐個來詳細說明:

1、通用性

在通用性上,主要考察該序列化協議是否支持跨平臺、跨語言的使用,同時兼顧考察業界的流行度及社區的活躍性。

2、可讀/易用性

在可讀、易用性上面,主要考察該序列化協議序列化之後是否人眼可讀,如 XML 和 JSON 就是人眼可讀的序列化框架,這會大大提高除錯的效率。同時,需要考察序列化框架所提供的 API 是否容易學習、呼叫。當然,在遠程呼叫 的場景下,可讀性不是一個重要因素。或者說,我們更多希望不可讀。來保證一定的安全性。

3、性能

性能指標,主要考慮序列化框架的時間複雜度和空間複雜度。

序列化之後的資料一般都是用於儲存或者網絡傳輸,空間占用大小決定了傳輸的效率。序列化通常情況下要在原有的資料上加上描述欄位,如果這個過程中引入的額外開銷過大,則在大規模分佈式系統中,很可能會造成巨大的額外空間開銷。

同時,為了提高系統的性能,是否耗費 CPU,解析和反解析二進制串的時間也是一個非常重要的指標。

4、可擴展性

主要考慮當系統準備升級,需要對物體的屬性進行變更,此時序列化協議能否快速支持,並且不會對原有的系統造成影響。如作為服務提供方的 API 接口入參中,增加了一個欄位,是否一定要強制所有的客戶端進行升級。這個會涉及到線上兼容性的問題。一般我們要求新增欄位,在客戶端尚未使用的情況下,不應該有序列化問題。

5、安全性

需要考察序列化協議是否支持跨局域網之間的安全訪問。是否存在一些安全漏洞。可以通過構造一些位元組陣列,使得服務端反序列化的時候,觸發某些安全漏洞,執行一些系統呼叫,或者越權操作。

  序列化使用方式分類

按照序列化的使用方式,可以分為自描述型序列化以及基於中間格式型序列化。

1、自描述型

所謂的自描述型,即在序列化的位元組流里有著完整的物件型別信息和屬性信息,可以在不依賴任何外界描述信息的前提下,只要拿到這個二進制流,就可以直接還原出原始物件。

類似的系列化產品有:hessianJSONXML 等。


例如,有如下一個物件 Person,Java 語言定義如下:

package com.sofa.test.Person;

public class Person {    private int age = 15;    private String name = “sofa”;
}

則使用 hessian 序列化後的位元組流如下:

M**com.sofa.test.PersonS**nameS**sofaS**ageI**b3 b2 b1 b0 z

上面的*和b3 b2 b1 b0都表示不可打印的二進制。從上面內容可以看出,按照相應規定就能從二進制串中反序列化出物件來。因為這裡面已經描述了型別,型別的欄位名,以及對應的值,這樣就可以直接反序列化了。

2、基於中間描述型

一般這種型別的序列化主要用於跨語言當中,比如 Protobuf以及 thrift 等等。在使用時都需要事先定義一個中間格式的檔案(IDL 檔案),然後根據不同語言的生成工具生成一個相應語言的可序列化類。以下是一個簡單的 Proto的描述檔案

message SofaApp{
    string appName = 1;
    repeated string authList = 2;
    repeated string serviceList = 3;
}

然後當需要反序列化時,根據 IDL 檔案及逆行相應的反序列化即可。格式是這樣

其中,圖中的用戶定義編號就是前面 proto中對每個欄位定義的編號。

  SOFARPC 序列化的設計與實現

SOFARPC 支持及將要支持的序列化協議有:hessianProtobufJson

序列化接口定義

在目前的 SOFARPC  5.4 分支中,已經支持的序列化協議有 hessianProtobuf。兩個序列化實現類繼承了 AbstractSerializer 抽象類,該抽象類又實現瞭如下的 Serializer 接口:

/**
 * 序列化器接口
 *
 * @author GengZhang
 */

@Extensible(coded = true)
@Unstable
public interface Serializer {    /**     * 序列化     *     * @param object  物件     * @param context 背景關係     * @return 序列化後的物件     * @throws SofaRpcException 序列化異常     */    public AbstractByteBuf encode(Object object, Map<String, String> context) throws SofaRpcException;    /**     * 反序列化,只有型別,傳回物件     *     * @param data    原始位元組陣列     * @param clazz   期望的型別     * @param context 背景關係     * @return 反序列化後的物件     * @throws SofaRpcException 序列化異常     */    public Object decode(AbstractByteBuf data, Class clazz, Map<String, String> context) throws SofaRpcException;    /**     * 反序列化,已有資料,填充欄位     *     * @param data     原始位元組陣列     * @param template 模板物件     * @param context  背景關係     * @throws SofaRpcException 序列化異常     */    public void decode(AbstractByteBuf data, Object template, Map<String, String> context) throws SofaRpcException;
}


從上面的接口定義可以看出,序列化方法傳入待序列化物件及相應的背景關係引數,最後生成序列化的物件。

反序列化則是多載的兩個方法,在傳入位元組資料及背景關係的時候,分別還可以傳入期望的型別或者模板。

序列化協議物件的獲取則通過 SerializerFactory 序列化工廠傳入序列化名稱獲取,獲取到的序列化協議物件再對傳入的資料進行相應的序列化與反系列化操作。

目前 SOFARPC 序列化支持協議,SOFA-Hessian,Protobuf,泛化呼叫序列化(hessian),Jackson。

  幾種序列化協議對比

序列化協議

簡要介紹

優點

缺點

SOFA-Hessian

hessian2 協議,安全改進

Java友好,性能較高

跨語言支持一般

Kryo

Kryo 框架

速度快,序列化後體積小

跨語言支持較複雜
,有一個限制,就是如果服務端增刪欄位,客戶端沒有更新會失敗,不支持無參建構式

Protobuf

中間描述型

跨語言,性能高

使用不夠友好,生成類可讀性差,需要工具輔助。

JDK

JVM 原生序列化支持

使用方便,無需引入額外依賴

速度慢,占空間,有安全問題,已經不再使用

JSON

各種 json 庫直接使用

跨語言,使用簡單,格式可讀

序列化結果大小較大,性能一般,可能存在反序列化漏洞。

這裡我們只介紹了幾種常見的,或者大家使用比較多的。對於一些其他不常見的序列化框架的性能和優缺點,可以參看參考文件中的 wiki,非常見的序列化框架可能存在更多的潛在限制,如果選型,需要特別註意。

  參考資料

  • SOFARPC 框架之總體設計與擴展機制

  • 序列化和反序列化:

    http://www.infoq.com/cn/articles/serialization-and-deserialization

  • 序列化性能比較:

    https://github.com/eishay/jvm-serializers/wiki

  • 高效的資料壓縮編碼方式 Protobuf:

    https://halfrost.com/protobuf_encode/

  結語

本文主要對 SOFARPC 序列化的內容進行了總括性的介紹。講述了序列化的定義及序列化框架的基本特性,同時對 SOFARPC 框架序列化的流程進行了說明。 

在設計和選擇 RPC 框架序列化協議的時候,可以根據實際情況進行選擇。

相關鏈接

SOFA 文件: http://www.sofastack.tech/

SOFA: https://github.com/alipay

SOFARPC: https://github.com/alipay/sofa-rpc

SOFABolt: https://github.com/alipay/sofa-bolt

SOFAMosn: https://github.com/alipay/sofa-mosn


  《剖析 | SOFARPC 框架》系列歷史文章



長按關註,獲取分佈式架構乾貨

歡迎大家共同打造 SOFAStack https://github.com/alipay

赞(0)

分享創造快樂