黑匣子
满天星
Fork me on GitHub

基于Netty的RPC架构学习笔记(八):protocol buff学习使用

简介

protocol buff是一种协议,是谷歌推出的一种序列化协议
Java序列化协议也是一种协议
两者的目的是,将对象序列化成字节数组,或者说是二进制数据

准备

protobuf的两个jar包、 protoc.exe(生成java的源代码,需要写protobuf的配置文件才能生成。)

protobuf配置文件

任意目录新建文件夹(任意名字)==》新建proto文件(任意名字,例如player.proto)后缀为proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//包名
option java_package = "com.proto";
//java_outer_classname的作用是将Paler类和Resource类整合到一个大的类中,一个大的Moudle中,名叫PlayerModule
option java_outer_classname = "PlayerModule";
//message相当于java中的class
message PBPlayer{
//在protobuf中没有int和long当时有对应的类型,int32是int类型,int64是long类型
//required意思是这个字段类型必须设置,加入不设置就会报错
//后面的123是key值,这些key值在message中是不能重复的,这里的key相当于{name:xiaoming}中的name
required int64 playerId = 1;

required int32 age = 2;

required string name = 3;
//repeated的意思是list,重复int的list
repeated int32 skills = 4;
}

message PBResource{
//金币
required int64 gold = 1;

required int32 energy = 2;
}

生成java代码

将上面的player.proto放到protoc.exe的同级目录===》在这个目录中新建build.bat
build.bat

1
2
3
4
5
//如果是生成c代码就是--cpp_out
//=后面是将java文件生成到哪个目录
protoc ./player.protp --java_out=./
//断点启动方便观察错误
pasue

点击build.bat会自动生成一个文件加com.proto ===》PlayerModule.java

举个🌰

新建项目==》libs用来放jar包==》新建proto目录用来放proto文件==》把上面的两个jar拷贝过去 ==》proto文件拷贝到proto目录中==》protoc.exe和build.bat拷贝到项目根目录
修改build.bat

1
2
protoc ./proto/*.protp --java_out=./src
pasue

在src下新建PB2Bytes.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package com.java;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class JAVA2Bytes {

public static void main(String[] args) throws Exception {
byte[] bytes = toBytes();
toPlayer(bytes);
}


/**
* 序列化
* @throws IOException
*/
public static byte[] toBytes() throws IOException{

Player player = new Player(101, 20, "peter");
player.getSkills().add(1001);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);

//写入对象
objectOutputStream.writeObject(player);

//获取 字节数组
byte[] byteArray = byteArrayOutputStream.toByteArray();
System.out.println(Arrays.toString(byteArray));
return byteArray;
}


/**
* 反序列化
* @param bs
* @throws Exceptipackage com.proto;

import java.util.Arrays;
import com.proto.PlayerModule.PBPlayer;
import com.proto.PlayerModule.PBPlayer.Builder;
/**

- protobuf学习

- @author
*/
public class PB2Bytes {

public static void main(String[] args) throws Exception {
byte[] bytes = toBytes();
toPlayer(bytes);

}

/**

- 序列化
*/
public static byte[] toBytes(){
//获取一个PBPlayer的构造器
Builder builder = PlayerModule.PBPlayer.newBuilder();
//设置数据
//上面skill是list,本应该setSkill,可是这里是setSkill(index,value),不是我们平常的skill
//可以直接addSkill(1001)添加1001这个技能
//前面文件文加了required前缀的必须要在这里设置值,不然会报错如下
//Message missing required field
builder.setPlayerId(101).setAge(20).setName("peter").addSkills(1001);
//构造出对象
PBPlayer player = builder.build();
//序列化成字节数组
byte[] byteArray = player.toByteArray();

System.out.println(Arrays.toString(byteArray));

return byteArray;
}

/**

- 反序列化

- @param bs

- @throws Exception
*/
public static void toPlayer(byte[] bs) throws Exception{

PBPlayer player = PlayerModule.PBPlayer.parseFrom(bs);

System.out.println("playerId:" + player.getPlayerId());
System.out.println("age:" + player.getAge());
System.out.println("name:" + player.getName());
System.out.println("skills:" + (Arrays.toString(player.getSkillsList().toArray())));
}
}

输出

1
2
3
4
5
[8,101,16,20,16,5,112,101,116,101,114,32,-23,7]
101
20
peter
1001

java序列化和反序列化

Player.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.java;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**

- 玩家对象

-
-
*
*/
public class Player implements Serializable{

/**

- */
private static final long serialVersionUID = -5248069984631225347L;

public Player(long playerId, int age, String name) {
this.playerId = playerId;
this.age = age;
this.name = name;
}

private long playerId;

private int age;

private String name;

private List<Integer> skills = new ArrayList<>();

public long getPlayerId() {
return playerId;
}

public void setPlayerId(long playerId) {
this.playerId = playerId;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Integer> getSkills() {
return skills;
}

public void setSkills(List<Integer> skills) {
this.skills = skills;
}
}

JAVA2Bytes.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.java;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class JAVA2Bytes {

public static void main(String[] args) throws Exception {
byte[] bytes = toBytes();
toPlayer(bytes);
}


/**
* 序列化
* @throws IOException
*/
public static byte[] toBytes() throws IOException{

Player player = new Player(101, 20, "peter");
player.getSkills().add(1001);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);

//写入对象
objectOutputStream.writeObject(player);

//获取 字节数组
//ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
//最终是写到byteArrayOutputStream中,所以通过byteArrayOutputStream获得。
byte[] byteArray = byteArrayOutputStream.toByteArray();
System.out.println(Arrays.toString(byteArray));
return byteArray;
}


/**
* 反序列化
* @param bs
* @throws Exception
*/
public static void toPlayer(byte[] bs) throws Exception{

ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(bs));
Player player = (Player)inputStream.readObject();

//打印
System.out.println("playerId:" + player.getPlayerId());
System.out.println("age:" + player.getAge());
System.out.println("name:" + player.getName());
System.out.println("skills:" + (Arrays.toString(player.getSkills().toArray())));
}

}

输出

1
2
3
4
5
[-84,-19,0,5,115,114,0,15,99,111,109,46,106,97,118,97,46,80,108........]
101
20
peter
1001

不同

java序列化出来的字节数组比proto buff的长很多,字节数组短的话能够减少很多的带宽。

为什么?

java中的字节数组包括这个类的类信息、这个类有哪个字段、协议头、每个类叫什么名称、每个类是什么类型的、最后的数值是多少等。

proto buff不许要有配置文件,java不需要只要双方都是java就行,所以proto buff其实是把类的名称、类型、字段等都写到了配置文件中,所以在序列化的时候集成了这些描述信息,所以字节很短。

proto buff分配空间是有伸缩性的,比如int 在内存中是 4 个字节,proto buff根据实际大小分配1-5个字节,从概率学的角度讲大部分可能都是一个或者两个字节

-------------The End-------------

本文标题:基于Netty的RPC架构学习笔记(八):protocol buff学习使用

文章作者:Leesin.Dong

发布时间:2019年03月10日 - 10:03

最后更新:2019年03月10日 - 23:03

原始链接:http://mmmmmm.me/2019-03-10-8.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

客官客官,不可以,你要对我负责~~~