在Dubbo中使用Kryo序列化协议
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
在Dubbo中使⽤Kryo序列化协议
Kryo是什么?
Kryo是⽤于Java的快速⾼效的⼆进制对象图序列化框架。
该项⽬的⽬标是⾼速,⼩尺⼨和易于使⽤的API。
不管是将对象持久保存到⽂件、数据库还是通过⽹络传输时,都可以尝试
Kryo。
Kryo还可以执⾏⾃动的深浅复制/克隆。
这是从对象到对象的直接复制,⽽不是从对象到字节的复制。
具体可以参考
在Dubbo中使⽤Kryo
本⽂基于Dubbo版本2.7.8
Dubbo⽀持⾮常多的序列化⽅式,如hession2、avro、FST等等,其中Dubbo官⽹推荐的序列化⽅式是Kryo,因为Kryo是⼀种⾮常成熟的序列化实现,已经在Twitter、Groupon、Yahoo以及多个著名开源项⽬(如Hive、Storm)中⼴泛的使⽤。
开始
在Dubbo中使⽤Kryo⾮常⽅便,⾸先引⼊依赖
// 解决⼀些Kryo特殊序列化,https:///magro/kryo-serializers
implementation 'de.javakaffee:kryo-serializers:0.43'
// ⾼性能序列化框架, https:///EsotericSoftware/kryo
implementation 'com.esotericsoftware:kryo:4.0.2'
如果只是简单使⽤,引⼊即可,如果要⽀持⼀些例如List接⼝,则需要引⼊,它针对⼀些特殊类为Kryo做了适配。
配置
在Dubbo中启⽤Kryo序列化⽅式,这⾥使⽤SpringBoot YML配置⽅式
protocol:
serialization: kryo
optimizer: org.hmwebframework.microservice.dubbo.serialize.SerializationOptimizerImpl
其中org.hmwebframework.microservice.dubbo.serialize.SerializationOptimizerImpl是指定Kryo序列化类,例如
public class SerializationOptimizerImpl implements SerializationOptimizer {
public Collection<Class> getSerializableClasses() {
List<Class> classes = new LinkedList<Class>();
classes.add(BidRequest.class);
classes.add(BidResponse.class);
classes.add(Device.class);
classes.add(Geo.class);
classes.add(Impression.class);
classes.add(SeatBid.class);
return classes;
}
}
到这,Dubbo使⽤Kryo就已经OK了。
为什么要定义SerializationOptimizer实现类?
⾸先我们分析下SerializationOptimizer
public interface SerializationOptimizer {
/**
* Get serializable classes
*
* @return serializable classes
* */
Collection<Class<?>> getSerializableClasses();
}
提供了⼀个接⼝⽅法,⽤于获取序列化的java类型列表,在DubboProtocol#optimizeSerialization中被使⽤
private void optimizeSerialization(URL url) throws RpcException {
// ...
try {
Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
// 判断是否为SerializationOptimizer实现类
if (!SerializationOptimizer.class.isAssignableFrom(clazz)) {
throw new RpcException("The serialization optimizer " + className + " isn't an instance of " + SerializationOptimizer.class.getName());
}
SerializationOptimizer optimizer = (SerializationOptimizer) clazz.newInstance();
if (optimizer.getSerializableClasses() == null) {
return;
}
// 将SerializationOptimizer中定义的类型列表,注册到SerializableClassRegistry
for (Class c : optimizer.getSerializableClasses()) {
SerializableClassRegistry.registerClass(c);
}
optimizers.add(className);
} catch (ClassNotFoundException e) {
// ...
}
}
接着,从SerializableClassRegistry中拿出注册的类型,进⾏Kryo的类型注册,可以看到SerializableClassRegistry#getRegisteredClasses被FST和Kryo使⽤,证明FST和Kryo都需要进⾏序列化类的注册,当然FST也⽀持不注册序列化类型。
Kryo类注册的具体细节,AbstractKryoFactory#create
// ...
for (Class clazz : registrations) {
kryo.register(clazz);
}
// 遍历取出SerializableClassRegistry的注册类,依次将类注册到Kryo
SerializableClassRegistry.getRegisteredClasses().forEach((clazz, ser) -> {
if (ser == null) {
kryo.register(clazz);
} else {
kryo.register(clazz, (Serializer) ser);
}
});
循环取出SerializableClassRegistry中的注册类进⾏注册,看到这⾥也能明⽩,为什么Dubbo官⽹的SerializationOptimizer例⼦需要使⽤LinkedList。
为什么Kryo需要进⾏类的注册,且保持顺序?
类的注册
在Dubbo这样的RPC框架进⾏通信时,性能瓶颈往往在于RPC传输过程中的⽹络IO耗时,提升⽹络IO的办法,⼀是加⼤带宽,⼆是减⼩传输的字节数,⽽⾼性能序列化框架可以做到的就是减⼩传输的字节数。
Kryo注册类的时候,使⽤了⼀个int类型的ID来与类进⾏关联,在序列化该类的实例时,⽤int ID来标识类型,反序列化该类时,同样通过int ID来找到类型,这⽐写出类名⾼效的多。
维持类注册顺序
Kryo注册类的时候,可以指定类关联的int ID,例如
Kryo kryo = new Kryo();
kryo.register(SomeClass.class, 10);
kryo.register(AnotherClass.class, 11);
kryo.register(YetAnotherClass.class, 12);
但是上⾯我们讲到,Dubbo对Kryo做了相当程度的集成,导致我们没法给类指定int ID,但是我们可以保证服务提供⽅和消费⽅类注册顺序的⼀致,间接地保证了int ID的⼀致性。
优化
反射获取待注册的类
在Dubbo中使⽤Kryo时,我们需要实现⼀个SerializationOptimizer,并提供⼀个注册类列表。
随着项⽬规模扩⼤,不可能时时刻
刻想着维护这个注册类列表,所以我们可以使⽤反射来⾃动获取这个注册类列表
引⼊依赖
// Java反射⼯具包
implementation 'org.reflections:reflections:0.9.11'
编写接⼝,
public interface KryoDubboSerializable
extends Serializable {
}
编写SerializationOptimizer实现类
@Slf4j
public abstract class AbstractSerializationOptimizerImpl
implements SerializationOptimizer {
private final List<Class<?>> classList;
public AbstractSerializationOptimizerImpl() {
var reflections = new Reflections(
new ConfigurationBuilder()
.forPackages(basePackage())
.addScanners(new SubTypesScanner())
);
this.classList = reflections.getSubTypesOf(KryoDubboSerializable.class)
.stream()
// Kryo序列化协议要求类注册顺序⼀致
.sorted(paring(Class::getSimpleName))
.collect(Collectors.toList());
("load {} classes to use kryo serializable", this.classList.size());
log.debug("kryo serializable classes: {}", this.classList.stream().map(Class::getSimpleName).collect(Collectors.joining(",")));
}
@Override
public Collection<Class<?>> getSerializableClasses() {
return classList;
}
/**
* 扫描包路径
*
* @return packages
*/
protected abstract String[] basePackage();
}
每次使⽤时,只需要继承AbstractSerializationOptimizerImpl,并提供待注册包路径(⽀持多个),待注册的类需要实现KryoDubboSerializable接⼝,这是为了在⼀定程度上提升灵活性(如果不需要注册到Kryo,不实现该接⼝即可)。
参考。