介绍
在RPC中,传输协议是必不可少的。协议其实规定了客户端和服务端怎么理解数据。有同学可能不是很理解序列化的意思,其实序列化的意思可以简单理解为怎么把一个结构化的数据给转化为字节数组。因为在网络传输的过程中,都是通过字节为基本单位传输的。下面介绍接种常见的序列化方式。
概念定义
序列化:把结构化数据转为字节数组
反序列化:把字节数组转为结构化数据
主要介绍
JSON,Protobuf,Hession,Thrift
环境说明
本次演示的平台:MacOS;开发工具:IDEA;构建方式:Maven;语言:Java
JSON序列化
说到这个,其实在早期大家用的最多的是XML,把对象转化为XML字符串,然后使用XML解析器解析数据。这个有兴趣的可以了解一下,现在用的不多。JSON序列化指的是把结构化转化为JSON数据,然后通过JSON数据传输。这种常用于HTTP请求,前端可以很好的理解JSON数据。
工具
其实JSON序列化的工具挺多的,比如:Jackson,fastJson等。使用fastJson做个演示。
依赖pom
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
结构定义
简单定义一个模型:Person类
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
注意这个类的属性一定要有get和set方法,可以使用lombok注解。因为fastjson的实现方式其实是通过反射遍历这个类的方法,拿到数据的。
序列化
public class Main {
public static void main(String[] args) {
Person p=new Person("test",22);
String s = JSON.toJSONString(p);
System.out.println(s);
}
}
序列化比较简单,就是直接通过toJSONString方法即可。
结果如下:
{"age":22,"name":"test"}
对于网络传输,也可以直接转为Byte数组,调用方法:toJSONBytes。
反序列化
public void rev(String str){
Person person = JSON.parseObject(str, Person.class);
}
反序列化也比较简单,直接parseObject,这里可以传入类型也可以不传。
或者是通过Byte数组:
public void rev(byte[]bytes){
Person person=JSON.parseObject(bytes,Person.class);
}
都是可以的。JSON序列化的特点就是比较简单,但是性能其实不是很好,对于一般的Web端接口完全是足够的,对于一些底层的网络传输,支持的力度不是很够。
Hession序列化
Hession是一种带有压缩的序列化方式。是Apache Dubbo的默认序列化方式,也是支持多语言的。Hession目前有两个版本:hession和hession2,hession2是在1的基础上做了优化,这个优化后续再聊。先看看怎么用。还是使用上述的Person模型,但是需要改动一点,因为Hessian序列化需要模型实现Serializable接口,所以Person需要实现一下这个接口。
序列化
public class Main {
public static void main(String[] args) throws Exception {
Person person = new Person("test", 22);
ByteArrayOutputStream os = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(os);
output.writeObject(person);
output.getBytesOutputStream().flush();
output.completeMessage();
output.close();
byte []data = os.toByteArray();
System.out.println(data.length);
}
}
序列化的方式也比较简单,就是把对象写到某个输出流,然后输出。
反序列化
public static void rev(byte []bytes)throws Exception{
ByteArrayInputStream inputStream=new ByteArrayInputStream(bytes);
Hessian2Input hessian2Input=new Hessian2Input(inputStream);
Object o = hessian2Input.readObject();
if(o instanceof Person){
o=(Person)o;
System.out.println(((Person) o).getName());
}
}
这样就完成了反序列化操作。
Hession的优点就是体积小,对性能要求高的地方可以采用。
protobuf
protobuf是google内部提出来的一种序列化方式。这个有点类似于thrift,个人感觉和thrift思想一样,都是通过一个描述文件生成不同语言的模型。来看一下如何使用。
描述文件
syntax = "proto3";
package com.yg.prot;
option optimize_for =SPEED;
option java_package = "com.yg.prot";
option java_outer_classname = "Stu";
message Student {
string name = 1;
int32 age = 2;
string address = 3;
}
这个文件其实定义了一个类,类里面定义了参数。至于为啥这样写,可以参考proto文件的写法,此处不再赘述/。
生成模型
有了proto文件,其实可以按照需要生成各种语言的模型,比如Java:
protoc --java_out . Person.proto
tips:注意这个脚本的写法:首先是protoc,这个是protobuf的命令,需要安装这个东西才能使用。安装方法有很多,如果是Mac就直接brew install protobuf,然后把env 配一下即可。其他平台也都有安装方式,可以直接下载编译好的可执行文件。
然后就会生成一个Java类。这个类很长,但是一般使用的是在proto文件里面定义的对象
序列化
首先可以使用建造者模式构建一个对象,其实这个对象自带的就有toByteArray方法:
public class Main {
public static void main(String[] args)throws Exception {
Stu.Student student = Stu.Student.newBuilder()
.setName("test")
.setAddress("111")
.setAge(22)
.build();
byte[] bytes = student.toByteArray();
System.out.println(bytes.length);
}
}
反序列化
反序列化也很简单
public static void rev(byte[]bytes)throws Exception{
Stu.Student student = Stu.Student.parseFrom(bytes);
System.out.println(student.getAge());
}
通过proto文件生成的模型是自带这些方法的,所以比较方便。
Protobuf和hessian一样,也是支持多种语言,protobuf的性能也是很好的。后续会出一些测试指标对比。Protobuf的优点就是,序列化出来的产物体积小,并且简单易用。相比hessian,更加符合编程者的思维。
Thrift
Thrift其实是一个rpc框架,但是它自带了序列化方式。序列化方式你可以自己指定。上篇博客里面有讲解,不再赘述。
总结
这篇文章介绍了现在RPC常用的序列化方式。目前的RPC框架大多都是支持用户自己选择序列化方式,而不是仅仅采用某一种。