Dubbo 源碼解讀 —— 可支持序列化及自定義擴展

1、概述

​ 從源碼中,咱們能夠看出來。目前,Dubbo 內部提供了 5 種序列化的方式,分別爲 fastjson、Hessian二、Kryo、fst 及 Java原生支持的方式 。java

​ 針對不一樣的序列化方式,對比內容以下:apache

名稱 優勢 缺點
Hessian 性能較好,多語言支持(推薦使用) Hessian的各版本兼容性很差,可能和應用使用的Hessian衝突,Dubbo內嵌了hessian3.2.1的源碼
fastjson 純文本,可跨語言解析,缺省採用FastJson解析 性能較差
kryo 速度快,序列化後體積小 跨語言支持較複雜
fst 兼容JDK序列化協議;序列化速度快;體積小;  
jdk Java原生支持;無需引入第三方類庫; 性能較差

​ 從成熟度上來講,Hessian 和 Java 相對成熟一些,可用於生產環境。編程

2、Dubbo serialization 實現

​ 總體的代碼結構比較清晰,按照不一樣類型的序列化方式,劃分紅了多個子模塊。根據模塊的名稱,想必你也可以知道該模塊是什麼序列化方式。接下來,咱們一一進行解讀:json

2.1 API 模塊

​ 他們的依賴關係如 UML 圖庫直接看出來,DataInput 和 DataOutput 接口類,主要是針對基本類型數據進行序列化和反序列化。ObjectInput 和 ObjectOutput 分別繼承 DataInput 和 DataOutput,主要是針對對象類型序列化和反序列化。設計模式

​ 抽象類 SerializableClassRegistry 主要是提供一個序列化統一的註冊中心。該類只有兩個方法:registerClass 和 getRegisteredClasses,前者是在系統進行初始化時,對可支持序列化方式的一個註冊功能,實際上就是將可序列化類(Class)加入到 Set 集合中!後者,主要是返回又有的註冊序列化 Class 類。app

​ Serialization 接口主要提供了四個方法:ide

byte getContentTypeId() # 獲取 contextType 編號ID 
 String getContentType() # 獲取 contextType 類型(kyro 的爲: "x-application/kryo") 
 ObjectOutput serialize(URL url, OutputStream output) # 根據輸入流信息,構造序列化對象 
 ObjectInput deserialize(URL url, InputStream input) # 根據輸出流信息,構造反序列化對象

​ 實現代碼相對簡單,讀者可自行查閱。工具

2.2 API 模塊

​ 在這裏,筆者主要以 Kyro 模塊爲主來解讀,其餘模塊雷同,再也不重述!性能

KryoSerialization 類實現了  Serialization 接口,並實現了其中的方法。 在  serialize() 方法中,建立了 Kryo 的 序列化類對象,在 deserialize() 方法中,建立了 KryoObjectInput 反序列化類對象。

    在參數中,傳遞有  URL url 目前這個參數沒有用到,暫不做過多解讀說明。
接下來看看 KryoSerialization 實現類:

public class KryoSerialization implements Serialization {

    @Override
    public byte getContentTypeId() {
        return 8;
    }

    @Override
    public String getContentType() {
        return "x-application/kryo";
    }

    @Override
    public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
        return new KryoObjectOutput(out);
    }

    @Override
    public ObjectInput deserialize(URL url, InputStream is) throws IOException {
        return new KryoObjectInput(is);
    }
}
咱們先來看看 KryoObjectOutput 類的實現。

在構造 KryoObjectOutput 傳入 了 outputStream 對象,初始化了 Kryo 的 Output 類實例, (以下,省略了部分代碼),並經過 KryoUtils 獲取 Kryo 對象。
public class KryoObjectOutput implements ObjectOutput, Cleanable {

    private Output output;
    private Kryo kryo;

    public KryoObjectOutput(OutputStream outputStream) {
        output = new Output(outputStream);
        this.kryo = KryoUtils.get();
    }

    @Override
    public void writeBool(boolean v) throws IOException {
        output.writeBoolean(v);
    }

    ........

    @Override
    public void cleanup() {
        KryoUtils.release(kryo);
        kryo = null;
    }
}

接下來,進入到 KryoUtils 中,看看 Kryo 是如何來構造出來的。ui

public class KryoUtils {
    private static AbstractKryoFactory kryoFactory = new ThreadLocalKryoFactory();

    public static Kryo get() {
        return kryoFactory.getKryo();
    }

    public static void release(Kryo kryo) {
        kryoFactory.returnKryo(kryo);
    }

    public static void register(Class<?> clazz) {
        kryoFactory.registerClass(clazz);
    }

    public static void setRegistrationRequired(boolean registrationRequired) {
        kryoFactory.setRegistrationRequired(registrationRequired);
    }
}

該工具類中,還提供了 register 和 setRegistrationRequired 類,以支持動態註冊功能。

這裏有有一個設計模式的使用,是「抽象工廠模式」,一共有上個繼承類,分別是:PrototypeKryoFactory、PooledKryoFactory、ThreadLocalKryoFactory。 這裏咱們以  ThreadLocalKryoFactory 做爲主要的入口來解讀,其餘兩個工廠類相對簡單一些。
![](https://oscimg.oschina.net/oscnet/f839bb21e0b44db1f2fadd1b04e0a309afb.jpg)

Kryo 的主要產生是經過 ThreadLocalKryoFactory 中的ThreadLocal 來實際進行的實例化,而且該類繼承了 AbstractKryoFactor 工廠類。當調用該工廠類的 getKryo() 方法時, holder 調用了其 get 方法,進行調用了ThreadLocal 內部的 initialValue() 方法,進而調用父類的 create() 方法,並開始了真正的初始化工做。

public class ThreadLocalKryoFactory extends AbstractKryoFactory {

    private final ThreadLocal<Kryo> holder = new ThreadLocal<Kryo>() {
        @Override
        protected Kryo initialValue() {
            return create();
        }
    };

    @Override
    public void returnKryo(Kryo kryo) {
        // do nothing
    }

    @Override
    public Kryo getKryo() {
        return holder.get();
    }
}

初始化的細節信息,讀者可自行在其抽象類中查閱。代碼相對簡單!

3、自定義擴展

目前,dubbo 是沒有支持 Google protocol buffer 序列化方式。用戶可按照 Dubbo 源碼接口及規範來實現。

首先,須要實現 API 模塊中的 Serialization 類,分別實現 ObjectInput 和 ObjectOut 接口。在實現類中編寫序列化及反序列化代碼。

最後,須要手動在 classpath 下建立 META-INF/dubbo/internel/org.apache.dubbo.common.serialize.Serialization 路徑。 文件名稱爲:

org.apache.dubbo.common.serialize.Serialization。在該文件中寫入本身的實現類,及該實現的 schema。例如:

protobuf=org.apache.dubbo.common.serialize.protocol.ProtobufSerialization

將本身的實現打包,注意打包的時候,必定要將 /META-INF 文件夾下的全部內容都打包進去。不然,將不會被 classloader 加載到,進而報錯!

4、總結

  1. 在 API 模塊中,將不一樣的處理劃分爲接口,針對接口編程,如:DataInput 和 DataOuput 接口分開,單一職責;
  2. 在 Kryo 的實現中,採用了抽象工廠模式;

=============================================================================

本文就到這裏,因爲本人水平有限,如有解讀錯誤之處,歡迎隨時拍磚。

相關文章
相關標籤/搜索