转:https://blog.wangqi.love/articles/Java/Protobuf%E7%9A%84%E4%BD%BF%E7%94%A8.html
最近在看canal源码的时候发现其中server与client数据通信使用了Protobuf协议,从前听过这个协议但是没用过,趁这个机会学习一下。
Protobuf是Google开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。
它不依赖于语言和平台并且可扩展性极强。相比于json和xml,Protobuf的使用更简单,传输的数据更小,序列化反序列化的性能更高。
本文简单介绍一下Protobuf在java中的使用。
安装
首先安装Protobuf。对于Mac OS只需要使用brew命令安装:
1 |
brew install protobuf |
定义消息格式
和json和xml等协议不同,Protobuf在使用之前需要先定义一个.proto文件作为消息的格式。
.proto文件的格式参考:https://blog.csdn.net/u011518120/article/details/54604615#ScalarValueTypes
假设我们要定义一个评论的消息格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
syntax = "proto3";
option java_package = "love.wangqi";
option java_outer_classname = "CommentProto";
message comment {
string id = 1;
string content = 2;
string articleTitle = 3;
string articleId = 4;
string articleUrl = 5;
string projectId = 6;
string categoryId = 7;
string commentUserId = 8;
string commentUserName = 9;
int32 likeCount = 10;
int64 sortNumber = 11;
}
|
Java使用
定义完.proto消息描述文件之后,执行以下命令生成Java代码:
1 |
protoc -I=src/main/java/love/wangqi --java_out=src/main/java comment.proto |
-
-I:proto文件所在的目录 -
--java_out:Java文件存放目录 - 最后是proto文件名称
执行完命令之后在src/main/java目录下会生成CommentProto.java类。
接下来我们就可以是用这个类了。
首先在pom文件中引入protobuf-java依赖:
1 2 3 4 5 |
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.7.0</version>
</dependency>
|
之后我们就可以使用protobuf:
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 |
public static void main(String[] args) throws InvalidProtocolBufferException {
// 构建Comment
CommentProto.comment.Builder commentBuilder = CommentProto.comment.newBuilder();
commentBuilder.setId("1");
commentBuilder.setContent("评论内容");
commentBuilder.setArticleTitle("稿件标题");
commentBuilder.setArticleId("11");
commentBuilder.setArticleUrl("http://localhost");
commentBuilder.setProjectId("123");
commentBuilder.setCategoryId("456");
commentBuilder.setCommentUserId("789");
commentBuilder.setCommentUserName("zhangsan");
commentBuilder.setLikeCount(10);
commentBuilder.setSortNumber(100000L);
CommentProto.comment comment = commentBuilder.build();
System.out.println(comment);
// 序列化,输出byte数组
byte[] bytes = comment.toByteArray();
System.out.println("序列化之后的数据大小:" + bytes.length);
// 根据byte数组,反序列化
CommentProto.comment comment1 = CommentProto.comment.parseFrom(bytes);
System.out.println("反序列化之后的数据:");
System.out.println(comment1);
}
|
Protobuf与Json的性能对比
上面我们提高过,Protobuf相比于Json传输的数据更小,性能更高。下面我们来实验一下。
性能测试父类,通过多线程循环执行之后统计执行时间:
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 |
public class Performance {
private int threadCount = 4;
private int loopCount = 10000000;
interface Action {
void run() throws Exception;
}
public void test(Action action) {
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
threadList.add(new Thread(() -> {
for (int i1 = 0; i1 < loopCount; i1++) {
try {
action.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}));
}
long start = System.currentTimeMillis();
for (Thread thread : threadList) {
thread.start();
}
for (Thread thread : threadList) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println("cost time " + (end - start) + "ms");
}
}
|
ProtoBuf的性能测试代码:
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 |
public class ProtoPerformance extends Performance {
CommentProto.comment.Builder commentBuilder;
CommentProto.comment comment;
public ProtoPerformance() {
commentBuilder = CommentProto.comment.newBuilder();
commentBuilder.setId("1");
commentBuilder.setContent("评论内容");
commentBuilder.setArticleTitle("稿件标题");
commentBuilder.setArticleId("11");
commentBuilder.setArticleUrl("http://localhost");
commentBuilder.setProjectId("123");
commentBuilder.setCategoryId("456");
commentBuilder.setCommentUserId("789");
commentBuilder.setCommentUserName("zhangsan");
commentBuilder.setLikeCount(10);
commentBuilder.setSortNumber(100000L);
comment = commentBuilder.build();
}
public void serialTest() {
System.out.println("serial:");
test(() -> comment.toByteArray());
}
public void deserialTest() {
System.out.println("deserial:");
byte[] commentBytes = comment.toByteArray();
System.out.println("byte size: " + commentBytes.length);
test(() -> {
try {
CommentProto.comment.parseFrom(commentBytes);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
});
}
}
|
Json的性能测试代码:
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 |
public class JsonPerformance extends Performance {
ObjectMapper mapper = new ObjectMapper();
Comment comment;
public JsonPerformance() {
comment = new Comment();
comment.setId("1");
comment.setContent("评论内容");
comment.setArticleTitle("稿件标题");
comment.setArticleId("11");
comment.setArticleUrl("http://localhost");
comment.setProjectId("123");
comment.setCategoryId("456");
comment.setCommentUserId("789");
comment.setCommentUserName("zhangsan");
comment.setLikeCount(10);
comment.setSortNumber(100000L);
}
public void serialTest() {
System.out.println("serial:");
test(() -> mapper.writeValueAsString(comment));
}
public void deserialTest() throws JsonProcessingException {
System.out.println("deserial:");
String json = mapper.writeValueAsString(comment);
byte[] bytes = json.getBytes();
System.out.println("byte size: " + bytes.length);
test(() -> mapper.readValue(json, Comment.class));
}
}
|
接下来执行Json与Protobuf的性能对比:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Main {
public static void main(String[] args) throws JsonProcessingException {
System.out.println("====json performance====");
JsonPerformance jsonPerformance = new JsonPerformance();
jsonPerformance.serialTest();
jsonPerformance.deserialTest();
System.out.println();
System.out.println("====protobuf performance====");
ProtoPerformance protoPerformance = new ProtoPerformance();
protoPerformance.serialTest();
protoPerformance.deserialTest();
}
}
|
执行结果如下:
从结果中我们可以看到protobuf序列化之后生成的数据要大大小于Json生成的数据,而且无论是序列化还是反序列化的性能都优于Json。
https://www.jianshu.com/p/bb3ac7e5834e
https://juejin.im/post/5b2a2880f265da598451eff3