dubbo序列化

dubbo序列化

dubbo做爲一個rpc框架支持豐富的序列化方式,本文簡單介紹dubbo的序列化。本文結構:html

  • 對象序列化是什麼意思?java

  • dubbo序列化apache

  • 幾個問題c#

對象序列化是什意思?

先來思考兩個問題:網絡

  1. 普通的Java對象的生命週期是僅限於一個JVM中的,只要JVM中止,這個對象也就不存在了,下次JVM啓動咱們還想使用這個對象怎麼辦呢?
  2. 或者咱們想要把一個對象傳遞給另一個JVM的時候,應該怎麼作呢?

這兩個問題的答案就是將該對象進行序列化,而後保存在文件中或者進行網絡傳輸到另外一個JVM,由另一個JVM反序列化成一個對象,而後供JVM使用。oracle

對象序列化就是將一個存在內存中的對象,轉換爲可存儲或者可傳輸的二進制流,而且根據序列化的規則能夠進行反序列化。通常來講須要保存的對象信息包括框架

  • 類的全限定名稱
  • 未被transparent修飾的字段值

將這些信息按照必定的規則轉換爲二進制以後進行網絡傳輸,在接收到的一端,就能夠根據這些信息先實例化這個類對應的對象,而後將對應的屬性值填入新構造的對象,在使用者看來就是使用的原來的對象。jvm

dubbo序列化

dubbo協議

dubbo支持不少種通訊協議,其中dubbo協議做爲默認的通訊協議,dubbo協議的協議格式以下ui

header 0-15 16 17 18 19-23 24-31 32-95 96-127
MAGIC = (short) 0xdabb req/resp two /one way event,是心跳仍是正常消息 serializationId 指定序列化的類型 狀態位, 消息類型爲response時,設置請求響應狀態 消息的id,long類型 body的length,int類型
body

header的字段格式是固定的,因此header的序列化方式也是固定的,header序列化過程以下(以request的encode爲例)spa

// com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec#encodeRequest
protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
        // 根據配置獲取序列化的協議,默認是hessian2
        Serialization serialization = getSerialization(channel);
        // header.
        byte[] header = new byte[HEADER_LENGTH];
        // set magic number.
        Bytes.short2bytes(MAGIC, header);

        // set request and serialization flag.
        header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
        // oneway仍是twoway
        if (req.isTwoWay()) header[2] |= FLAG_TWOWAY;
        // event種類
        if (req.isEvent()) header[2] |= FLAG_EVENT;

        // set request id.
        Bytes.long2bytes(req.getId(), header, 4);

        // encode request data.
        int savedWriteIndex = buffer.writerIndex();
        buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
        ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
        ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
        if (req.isEvent()) {
            encodeEventData(channel, out, req.getData());
        } else {
            encodeRequestData(channel, out, req.getData());
        }
        out.flushBuffer();
        if (out instanceof Cleanable) {
            ((Cleanable) out).cleanup();
        }
        bos.flush();
        bos.close();
        int len = bos.writtenBytes();
        checkPayload(channel, len);
        // 消息長度,在消息序列化完成後才能肯定消息體body的長度
        Bytes.int2bytes(len, header, 12);

        // write
        buffer.writerIndex(savedWriteIndex);
        buffer.writeBytes(header); // write header.
        buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
    }

header的序列化方式是固定的,可是消息體body能夠指定不一樣的序列化方式,因爲消息體能夠由用戶自定義,因此多是各類類型。一種序列化協議須要支持序列化和反序列化各類類型,包括基礎類型和類類型,好比:null、long、int、String、List、Map、enum、自定義類等。

dubbo中序列化協議都實現了下面的接口

com.alibaba.dubbo.common.serialize.Serialization

經過SPI擴展能夠實現不一樣的協議,默認的SPI擴展是hessian2

com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization

dubbo中的序列化、反序列化都要實現下面的接口

com.alibaba.dubbo.common.serialize.ObjectOutput
com.alibaba.dubbo.common.serialize.ObjectInput

好比hessian序列化和反序列化

com.alibaba.dubbo.common.serialize.support.hessian.Hessian2ObjectOutput
com.alibaba.dubbo.common.serialize.support.hessian.Hessian2ObjectInput

這兩個類裏面的方法是經過調用Hessian2Output、Hessian2Input方法實現的,Hessian2Output和Hessian2Input實現了hessian協議。具體的協議內容能夠參考這個

幾個問題

爲何參數對象都要實現Serializable接口(使用dubbo協議,默認的序列化方式hessian的時候)?

由於dubbo使用hessian序列化方式的時候,對象的序列化使用的是JavaSerializer

com.alibaba.com.caucho.hessian.io.SerializerFactory#getDefaultSerializer
com.alibaba.com.caucho.hessian.io.SerializerFactory#getSerializer
com.alibaba.com.caucho.hessian.io.Hessian2Output#writeObject

獲取默認的序列化方式的時候會判斷該參數是否實現了Serializable接口

protected Serializer getDefaultSerializer(Class cl) {
    if (_defaultSerializer != null)
        return _defaultSerializer;

    // 判斷是否實現了Serializable接口
    if (!Serializable.class.isAssignableFrom(cl)
        && !_isAllowNonSerializable) {
        throw new IllegalStateException("Serialized class " + cl.getName() + " must implement java.io.Serializable");
    }

    return new JavaSerializer(cl, _loader);
}

若是沒有實現Serializable接口的話就拋出異常,因此在聲明參數的時候,參數是一個類,這個類必須實現Serializable接口。

關於serialVersionUID

serialVersionUID是Java原生序列化時候的一個關鍵屬性,可是在不使用Java原生序列化的時候,這個屬性是沒有被用到的,好比基於hessian協議實現的序列化方式中沒有用到這個屬性。

這裏說的Java原生序列化是指使用下面的序列化方式和反序列化方式

java.io.ObjectOutputStream
java.io.ObjectInputStream

在使用原生序列化的時候,serialVersionUID起到了一個相似版本號的做用,在反序列化的時候判斷serialVersionUID若是不相同,會拋出InvalidClassException。

若是在使用原生序列化方式的時候官方是強烈建議指定一個serialVersionUID的,若是沒有指定,在序列化過程當中,jvm會自動計算出一個值做爲serialVersionUID,因爲這種運行時計算serialVersionUID的方式依賴於jvm的實現方式,若是序列化和反序列化的jvm實現方式不同可能會致使拋出異常InvalidClassException,因此強烈建議指定serialVersionUID。

參考

dubbo文檔

Understand the serialVersionUID

相關文章
相關標籤/搜索