前言:
前文中介绍了有关Dubbo协议的相关知识。Dubbo协议是Dubbo框架自定义的一种协议,类似于HTTP协议,也是一种应用层协议。
从Dubbo框架结构来看(参考:https://dubbo.apache.org/zh/docsv2.7/dev/design/),其位于如下位置:
?
服务消费者和提供者依据协议的模式来进行消息的发送和接收。
在消息发送的过程中,需要对消息体进行序列化操作,而Dubbo本身提供了多种序列化方式,本文就来简单了解下这些序列化方式。
1.获取序列化方式的入口
之前DubboCodec中我们有简单说明过,入口在以下位置:
public class ExchangeCodec extends TelnetCodec {
// 发送数据之前进行编码操作
public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
if (msg instanceof Request) {
// 关注点在这里
encodeRequest(channel, buffer, (Request) msg);
} else if (msg instanceof Response) {
encodeResponse(channel, buffer, (Response) msg);
} else {
super.encode(channel, buffer, msg);
}
}
protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
// 在这里获取序列化方式
Serialization serialization = getSerialization(channel);
...
}
protected Serialization getSerialization(Channel channel) {
// 具体见1.1
return CodecSupport.getSerialization(channel.getUrl());
}
}
1.1 CodecSupport.getSerialization()?
public class CodecSupport {
public static Serialization getSerialization(URL url) {
return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(
url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
}
}
同样是通过SPI的方式来获取序列化方式。如果在url中没有配置的话,则默认是hessian2方式。
2.Dubbo的序列化方式
通过Serialization接口的实现类,我们可以看到Dubbo支持的序列化方式如下:
?
以上是常用的几种序列化方式,至于这些序列化方式的使用场景,在?成熟度 | Apache Dubbo? 中有说明
Feature | Maturity | Strength | Problem | Advise | User | Hessian Serialization | Stable | 性能较好,多语言支持(推荐使用) | Hessian的各版本兼容性不好,可能和应用使用的Hessian冲突,Dubbo内嵌了hessian3.2.1的源码 | 可用于生产环境 | Alibaba | Dubbo Serialization | Tested | 通过不传送POJO的类元信息,在大量POJO传输时,性能较好 | 当参数对象增加字段时,需外部文件声明 | 试用 | | Json Serialization | Tested | 纯文本,可跨语言解析,缺省采用FastJson解析 | 性能较差 | 试用 | | Java Serialization | Stable | Java原生支持 | 性能较差 | 可用于生产环境 | |
而如果是纯java应用,则可以考虑使用性能更高的Kryo/FST序列化方式,在官网有关于其性能测试结果,具体在?Kryo 和 FST 序列化 | Apache Dubbo?
最终结果如下两张图:
?
?
由以上测试结果可以看到:Kryo/FST相对其他序列化方式有显著的改进。
3.序列化源码解析
序列化的方式有这么多种,笔者不一一介绍了,挑一个大家都比较熟悉的,说明一下序列化的过程即可。
那我们就选择JavaSerialization(JDK原生的序列化方式)来做说明
3.1 序列化的过程
首先我们从ExchangeCodec.java来看下序列化的过程,这样可以更针对性的学习序列化的相关实现
public class ExchangeCodec extends TelnetCodec {
protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
// 获取序列化实现方式
Serialization serialization = getSerialization(channel);
...
// 通过serialize()方法获取ObjectOutput对象
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
if (req.isEvent()) {
encodeEventData(channel, out, req.getData());
} else {
// 序列化请求data
encodeRequestData(channel, out, req.getData(), req.getVersion());
}
// 刷新缓存
out.flushBuffer();
...
}
protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
encodeRequestData(out, data);
}
protected void encodeRequestData(ObjectOutput out, Object data) throws IOException {
// 最终通过调用ObjectOutput.writeObject()方法来实现序列化
out.writeObject(data);
}
}
通过分析可知:序列化的过程主要分为以下几个步骤:
* 获取序列化实现方式Serialization
* 通过Serialization.serialize() 方法获取ObjectOutput对象,后续都是通过ObjectOutput来操作
* 通过ObjectOutput.writeObject() 实现对请求对象的序列化
* 刷新ObjectOutput
通过以上序列化过程,我们参照JavaSerialization来观察其实现。
3.2?JavaSerialization
public class JavaSerialization implements Serialization {
// Constants.java中定义 byte JAVA_SERIALIZATION_ID = 3;
public byte getContentTypeId() {
return JAVA_SERIALIZATION_ID;
}
@Override
public String getContentType() {
return "x-application/java";
}
// 获取ObjectOutput对象
public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
return new JavaObjectOutput(out);
}
@Override
public ObjectInput deserialize(URL url, InputStream is) throws IOException {
return new JavaObjectInput(is);
}
}
JavaSerialization中所使用的ObjectOutput实现类为JavaObjectOutput,后续的序列化操作都交由JavaObjectOutput来实现
3.2.1 JavaObjectOutput的使用
public class JavaObjectOutput extends NativeJavaObjectOutput {
public JavaObjectOutput(OutputStream os) throws IOException {
// 创建一个JDK原声的ObjectOutputStream对象,用于序列化对象
super(new ObjectOutputStream(os));
}
public JavaObjectOutput(OutputStream os, boolean compact) throws IOException {
super(compact ? new CompactedObjectOutputStream(os) : new ObjectOutputStream(os));
}
@Override
public void writeUTF(String v) throws IOException {
if (v == null) {
getObjectOutputStream().writeInt(-1);
} else {
getObjectOutputStream().writeInt(v.length());
getObjectOutputStream().writeUTF(v);
}
}
// 主要看这个方法
public void writeObject(Object obj) throws IOException {
if (obj == null) {
getObjectOutputStream().writeByte(0);
} else {
// 本质上直接调用了ObjectOutputStream.writeObject()方法
getObjectOutputStream().writeByte(1);
getObjectOutputStream().writeObject(obj);
}
}
@Override
public void flushBuffer() throws IOException {
// 直接调用ObjectOutputStream.flush()进行刷新
getObjectOutputStream().flush();
}
}
总结:
JavaSerialization的实现并不算复杂,本质上,所有的序列化操作都交由JDK原声的ObjectOutputStream来执行。
而其他的序列化方式也是类似的,通过Serialization提供一个标准模板,具体的序列化操作都交由具体的实现来完成即可。
|