dubbo做爲一個rpc框架支持豐富的序列化方式,本文簡單介紹dubbo的序列化。本文結構:html
對象序列化是什麼意思?java
dubbo序列化apache
幾個問題c#
先來思考兩個問題:網絡
這兩個問題的答案就是將該對象進行序列化,而後保存在文件中或者進行網絡傳輸到另外一個JVM,由另一個JVM反序列化成一個對象,而後供JVM使用。oracle
對象序列化就是將一個存在內存中的對象,轉換爲可存儲或者可傳輸的二進制流,而且根據序列化的規則能夠進行反序列化。通常來講須要保存的對象信息包括框架
將這些信息按照必定的規則轉換爲二進制以後進行網絡傳輸,在接收到的一端,就能夠根據這些信息先實例化這個類對應的對象,而後將對應的屬性值填入新構造的對象,在使用者看來就是使用的原來的對象。jvm
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協議。具體的協議內容能夠參考這個
由於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是Java原生序列化時候的一個關鍵屬性,可是在不使用Java原生序列化的時候,這個屬性是沒有被用到的,好比基於hessian協議實現的序列化方式中沒有用到這個屬性。
這裏說的Java原生序列化是指使用下面的序列化方式和反序列化方式
java.io.ObjectOutputStream java.io.ObjectInputStream
在使用原生序列化的時候,serialVersionUID起到了一個相似版本號的做用,在反序列化的時候判斷serialVersionUID若是不相同,會拋出InvalidClassException。
若是在使用原生序列化方式的時候官方是強烈建議指定一個serialVersionUID的,若是沒有指定,在序列化過程當中,jvm會自動計算出一個值做爲serialVersionUID,因爲這種運行時計算serialVersionUID的方式依賴於jvm的實現方式,若是序列化和反序列化的jvm實現方式不同可能會致使拋出異常InvalidClassException,因此強烈建議指定serialVersionUID。
參考