Dubbo 源码解读 —— 可支持序列化及自定义扩展
2018-07-16 18:26
399 查看
一、概述
从源码中,我们可以看出来。目前,Dubbo 内部提供了 5 种序列化的方式,分别为 fastjson、Hessian2、Kryo、fst 及 Java原生支持的方式 。 针对不同的序列化方式,对比内容如下:
名称 | 优点 | 缺点 |
---|---|---|
Hessian | 性能较好,多语言支持(推荐使用) | Hessian的各版本兼容性不好,可能和应用使用的Hessian冲突,Dubbo内嵌了hessian3.2.1的源码 |
fastjson | 纯文本,可跨语言解析,缺省采用FastJson解析 | 性能较差 |
kryo | 速度快,序列化后体积小 | 跨语言支持较复杂 |
fst | 兼容JDK序列化协议;序列化速度快;体积小; | |
jdk | Java原生支持;无需引入第三方类库; | 性能较差 |
二、Dubbo serialization 实现
整体的代码结构比较清晰,按照不同类型的序列化方式,划分成了多个子模块。根据模块的名称,想必你也能够知道该模块是什么序列化方式。接下来,我们一一进行解读:2.1 API 模块
他们的依赖关系如 UML 图库直接看出来,DataInput 和 DataOutput 接口类,主要是针对基本类型数据进行序列化和反序列化。ObjectInput 和 ObjectOutput 分别继承 DataInput 和 DataOutput,主要是针对对象类型序列化和反序列化。 抽象类 SerializableClassRegistry 主要是提供一个序列化统一的注册中心。该类只有两个方法:registerClass 和 getRegisteredClasses,前者是在系统进行初始化时,对可支持序列化方式的一个注册功能,实际上就是将可序列化类(Class)加入到 Set 集合中!后者,主要是返回又有的注册序列化 Class 类。
Serialization 接口主要提供了四个方法:
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 是如何来构造出来的。
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(); } }
初始化的细节信息,读者可自行在其抽象类中查阅。代码相对简单!
三、自定义扩展
目前,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 加载到,进而报错!
四、总结
在 API 模块中,将不同的处理划分为接口,针对接口编程,如:DataInput 和 DataOuput 接口分开,单一职责;在 Kryo 的实现中,采用了抽象工厂模式;
=============================================================================
本文就到这里,由于本人水平有限,若有解读错误之处,欢迎随时拍砖。
相关文章推荐
- Dubbo源码分析(三):自定义Schema--基于Spring可扩展Schema提供自定义配置支持(spring配置文件中 配置标签支持)
- Dubbo基于Spring可扩展 Schema提供自定义配置支持分析
- 【dubbo源码解读系列】之二 dubbo代码启动入口解析(自定义main方法)
- Dubbo 源码解读——自定义 Classloader 之 ExtensionLoader
- dubbo序列化过程源码分析:
- 基于Spring可扩展Schema提供自定义配置支持(转载)
- Caffe源码解读(十一):自定义一个layer
- 基于Spring可扩展Schema提供自定义配置支持
- easyUI扩展datagrid支持按列点击表头自定义排序
- JAVA源码解读---HashMap目录扩展的奥秘
- 基于Spring可扩展Schema提供自定义配置支持
- 当当网开源Dubbox,扩展Dubbo服务框架支持REST风格远程调用
- dubbo源码深度解读一之common模块
- 支持自定义扩展名或空扩展名的Dispatcher
- 分布式事务 TCC-Transaction 源码分析 —— Dubbo 支持
- 【JDK源码分析】04-使用Externalizable实现自定义序列化
- 基于Spring可扩展Schema提供自定义配置支持(spring配置文件中 配置标签支持)
- dubbo源码解析(一): 扩展点加载(ExtensionLoader)
- dubbo的内核源码UML(容器启动以及Spring的shceme扩展)
- 基于Spring可扩展Schema提供自定义配置支持