Protobuf的使用

转:https://blog.wangqi.love/articles/Java/Protobuf%E7%9A%84%E4%BD%BF%E7%94%A8.html

最近在看canal源码的时候发现其中server与client数据通信使用了Protobuf协议,从前听过这个协议但是没用过,趁这个机会学习一下。

Protobuf是Google开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。

它不依赖于语言和平台并且可扩展性极强。相比于jsonxml,Protobuf的使用更简单,传输的数据更小,序列化反序列化的性能更高。

本文简单介绍一下Protobuf在java中的使用。

安装

首先安装Protobuf。对于Mac OS只需要使用brew命令安装:

1
brew install protobuf

定义消息格式

jsonxml等协议不同,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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值