手动实现一个RPC框架(四):序列化协议选择

手动实现RPC系列文章

前面三篇章的文章,我们已经了解学习了RPC是什么,以及RPC的原理。实现一个RPC框架需要用到哪些技术。有兴趣的小伙伴们可以点击以下链接看见这部分的所有内容

手动实现RPC框架_种一棵橙子树的博客-CSDN博客

另在此申明,我是基于GitHub作者 Java Guide的开源作品来实现的,请大家直接点击链接在GitHub上进行学习。

Snailclimb/guide-rpc-framework: A custom RPC framework implemented by Netty+Kyro+Zookeeper.(一款基于 Netty+Kyro+Zookeeper 实现的自定义 RPC 框架-附详细实现过程和相关教程。) (github.com)


前言

前面的文章我们提到了序列化这个概念,我们知道,Java的对象要在网络中进行传输就离不开序列化,因为网络传输只能够进行二进制字节流的传输。而通过序列化我们就可以把数据结构或者对象转换成二进制字节流,而反序列化可以再把这些二进制字节流转换成对象。

在我们设计的RPC框架中,由于要通过网络实现远程调用,就需要通过代理对象,将方法,参数等信息封装成能够在网络中传输的消息体,然后这个消息体要进行序列化操作以后,才能够在我们的服务消费端和服务提供方之间进行传输使用。

所以下面的内容,就详细讲解一下序列化操作和Java中序列化方式,以及我们的序列化协议选择。

一、序列化的使用场景?

在我们的实际开发过程中,有以下几个方面会用到序列化和反序列化的场景

1.对象在进行网络传输的过程中需要先被序列化,接收到序列化对象之后再进行反序列化。

2.将对象存储到文件中需要先进行序列化,对象从文件中取出进行反序列化。

3.将对象存储到缓存数据库(Redis,MongoDB)时需要用到序列化,将对象从缓存数据库中读取出来需要反序列化。

序列化协议对应TCP/IP四层模型的那一层

我们知道,序列化和反序列化是用于网络传输过程中的操作,那么规定序列化操作的我们可以称之为序列化协议,我们知道网络传输中不同协议对应不同的层面,那么序列化协议在哪一层呢。

在OSI七层协议模型中

 OSI七层模型中的表示层负责对应用层的用户数据进行处理,转换成二进制流往下进行传输,而反过来,它接受二进制字节流又转换成用户数据提供给应用层。这是不是就很像我们的序列化和反序列化操作呢。

在OSI七层模型中,应用层,表示层和会话层都对应的是TCP/IP模型中的应用层,所以序列化和反序列化在TCP/IP模型中就是位于应用层的协议。

 

二、常见的序列化协议

前面我们提到了,序列化方式最简单的一种就是直接实现JDK自带的序列化接口Serializable就可以了,但是这种方式不支持跨语言调用,而且性能比较低。现在常用的序列化协议有 hessian,kyro,protostuff。

另外还有JSON和XML这种文本类序列化方式,可读性比较好,但是性能也比较差。

Serializable接口序列化

以一个代码例子来进行说明

public class RpcRequest implements Serializable{
    private static final long serialVersionUID = 1L;
    private String requestId;
    private String interfaceName;
    private String methodName;
}

 这里的 serialVersionUID 是我们指定的序列化数据的版本,当对这个类的对象进行序列化操作的时候,serialVersionUID 会被写入到二进制序列中,当反序列化的时候会检查这个二进制序列的serialVersionUID 是否和当前类的 serialVersionUID 相同,如果相同才会正常进行,所以我们把它定义为 静态final 常量。

而如果二进制序列的serialVersionUID 和当前类的 serialVersionUID不同,就会抛出 InvalidClassException 异常。一般我们会手动指定serialVersionUID,如果没有手动指定,编译器会自动生成默认的serialVersionUID。

现实中我们比较少用Java自带的序列化接口进行序列化。

原因是1.不支持跨语言调用,其他语言无法使用

2.相比其他序列化框架封装的序列化功能性能较低,主要原因是序列化后的字节数组体积较大,传输成本高。

Kryo

Kryo是一个高性能的序列化/反序列化工具,由于其变长存储的特性,并且使用了字节码生成机制,拥有较高的运行速度和较小的字节码体积。

Kryo作为一个成熟的序列化工具,在Twitter,Groupon,Yahoo等多个著名开源项目中都有广泛使用。

而本次我动手实现的RPC框架(GitHub作者 Java Guide的开源作品)也是使用Kyro进行序列化操作的。GitHub地址:Netty+Kyro+Zookeeper.(一款基于 Netty+Kyro+Zookeeper 实现的自定义 RPC 框架-附详细实现过程和相关教程。) (github.com)

本次RPC框架使用到的Kyro序列化相关代码如下,具体如何进行使用,会在后面实现的过程中进行讲解。

@Slf4j
public class KryoSerializer implements Serializer{
    private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
        Kryo kryo = new Kryo();
        kryo.register(RpcResponse.class);
        kryo.register(RpcRequest.class);
        return kryo;
    });
    @Override
    public byte[] serialize(Object obj){
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Output output = new Output(byteArrayOutputStream)){
            Kryo kryo = KryoThreadLocal.get();
            //将对象序列化成byte数组
            kryo.writeObject(output,obj);
            kryoThreadLocal.remove();
            return output.toBytes();
        }catch (Exception e){
            throw new SerialException("序列化失败");
        }
    }
    @Override
    public <T> T deserialize(byte[] bytes,Class<T> clazz){
        try(ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            Input input = new Input(byteArrayInputStream)){
            Kyro kyro = kryoThreadLocal.get();
            //反序列化
            Object o = kryo.readObject(input,clazz);
            kryoThreadLocal.remove();
            return clazz.cast(o);
        }catch (Exception e){
            throw new SerialException("反序列化失败");
        }
    }
}

 

Kryo的Github地址:EsotericSoftware/kryo: Java binary serialization and cloning: fast, efficient, automatic (github.com)

Protobuf

Protobuf 产于Google,性能不错,支持多种语言,并且支持跨平台,但是使用过程比较繁琐,需要我们自定义IDL文件和生成对应的序列化代码。虽然繁琐,但是可以避免序列化漏洞的风险

Protobuf 包含序列化格式的定义,各种语言的库以及一个IDL编译器。正常情况下需要自定义proto文件,然后使用IDL编译器编译成我们使用的语言。

一个简单的proto文件如下:

//protobuf版本
syntax = "proto3";
//SerachRequest 会被编译成不同编程语言对应的对象,比如Java中的class
message Person{
   string name = 1;
   int age = 2;
}

 

 GitHub地址:protocolbuffers/protobuf: Protocol Buffers - Google's data interchange format (github.com)

ProtoStuff 

protobuf的哥哥,也是Google出品的,提供了更多的功能,易用性更高。然而效率并不比他的弟弟差 Github地址:protostuff/protostuff: Java serialization library, proto compiler, code generator (github.com)

hession

hession是一个轻量级的自定义二进制RPC协议。也支持跨语言操作,是Dubbo默认的序列化方式

 


 

总结

Kryo是专门针对Java语言使用的序列化方式,性能良好,如果我们的应用只针对Java语言的编写的话,可以考虑使用。在Dubbo官网的文章中也有推荐使用Kryo作为生产环境的序列化方式。

 而其他的序列化协议都是支持跨语言的序列化方式,如果有跨语言需求的话,可以考虑使用。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值