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

螞蟻金服分散式架構發表於2018-11-01

前言

在應用服務化架構中,RPC 框架是非常重要的基礎元件。而在 RPC 框架當中,序列化(以及反序列化)又是必不可少的一環。因為序列化的效能對整體框架效能有比較大的影響,之前的文章中,我們已經詳細剖析了 SOFARPC 各個核心功能模組的實現原理,想必大家已經很清楚 RPC 的呼叫流程。
在整個 RPC 呼叫流程當中,序列化及反序列化起到了承上啟下的作用。序列化時,RPC客戶端把待呼叫的方法和引數物件轉換為網路上可傳輸的位元組序列,為進一步的編解碼提供原料。反序列化時,把從網路上接收到且已經解碼了的位元組序列轉換成物件,便於 RPC 服務端呼叫。
本文將從序列化概述、序列化協議特性、序列化使用方法分類、SOFARPC 序列化的設計及實現、幾種序列化協議對比等方面介紹及對比序列化及其在 SOFARPC 中的應用。


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

序列化概述

RPC 呼叫通過網路傳輸相關的呼叫方法及引數,在這個網路傳輸過程中,記憶體中的物件是無法直接傳輸的,只有二進位制位元組才能在網路上傳輸。而為了實現呼叫物件在網路上的傳輸,必須通過序列化實現物件 -> 位元組的過程,以及反序列化實現位元組 -> 物件的過程。在網路協議模型中,序列化屬於應用層協議的一部分。
如下列定義:
序列化:將資料結構或者物件轉換成二進位制串的過程。
反序列化:將序列化過程中生成的二進位制串轉換成資料結構或者物件的過程。
在上述定義中,二進位制位元組陣列專指 Java 語言中的 byte[]

序列化協議特性

每種序列化協議都有其優點和缺點,在對一個序列化協議進行衡量評判時,通常由如下一些指標可以參考:
指標
說明
重要性
通用性
是否跨平臺,社群如何
中高
可讀
序列化格式是否可讀
中低
易用性
是否簡單易用
中高
效能
序列化後的大小和壓縮 CPU消耗
中高
可擴充套件性
是在允許欄位修改
安全性
是否存在一些無法修復的漏洞
以下逐個來詳細說明:

通用性

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

可讀/易用性

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

效能

效能指標,主要考慮序列化框架的時間複雜度和空間複雜度。
序列化之後的資料一般都是用於儲存或者網路傳輸,空間佔用大小決定了傳輸的效率。序列化通常情況下要在原有的資料上加上描述欄位,如果這個過程中引入的額外開銷過大,則在大規模分散式系統中,很可能會造成巨大的額外空間開銷。
同時,為了提高系統的效能,是否耗費 CPU,解析和反解析二進位制串的時間也是一個非常重要的指標。

可擴充套件性

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

安全性

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

序列化使用方式分類

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

自描述型

所謂的自描述型,即在序列化的位元組流裡有著完整的物件型別資訊和屬性資訊,可以在不依賴任何外界描述資訊的前提下,只要拿到這個二進位制流,就可以直接還原出原始物件。
類似的系列化產品有: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都表示不可列印的二進位制。從上面內容可以看出,按照相應規定就能從二進位制串中反序列化出物件來。因為這裡面已經描述了型別,型別的欄位名,以及對應的值,這樣就可以直接反序列化了。

基於中間描述型

一般這種型別的序列化主要用於跨語言當中,比如 Protobuf以及 thrift 等等。在使用時都需要事先定義一箇中間格式的檔案(IDL 檔案),然後根據不同語言的生成工具生成一個相應語言的可序列化類。以下是一個簡單的 Proto的描述檔案
message SofaApp{
    string appName = 1;
    repeated string authList = 2;
    repeated string serviceList = 3;
}
複製程式碼
然後當需要反序列化時,根據 IDL 檔案及逆行相應的反序列化即可。格式是這樣
【剖析 | SOFARPC 框架】系列之 SOFARPC 序列化比較
其中,圖中的使用者定義編號就是前面 proto中對每個欄位定義的編號。

SOFARPC 序列化的設計與實現

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

序列化介面定義

在目前的 SOFARPC 5.4 分支中,已經支援的序列化協議有 hessianProtobuf。兩個序列化實現類繼承了 AbstractSerializer 抽象類,該抽象類又實現瞭如下的 Serializer 介面:
/**
 * 序列化器介面
 *
 * @author <a href=mailto:zhanggeng.zg@antfin.com>GengZhang</a>
 */
@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 框架之總體設計與擴充套件機制:mp.weixin.qq.com/s/ZKUmmFT0N…
序列化和反序列化:www.infoq.com/cn/articles…
序列化效能比較:github.com/eishay/jvm-…
高效的資料壓縮編碼方式 Protobuf:halfrost.com/protobuf_en…

結語

本文主要對 SOFARPC 序列化的內容進行了總括性的介紹。講述了序列化的定義及序列化框架的基本特性,同時對 SOFARPC 框架序列化的流程進行了說明。 在設計和選擇 RPC 框架序列化協議的時候,可以根據實際情況進行選擇。

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

長按關注,獲取分散式架構乾貨

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


相關文章