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。
参考