如何在Java中使用protobufjava进行protobuf生成器编写

作者:禅与计算机程序设计艺术

最近随着微服务架构的流行,越来越多的人选择了基于RPC框架(比如dubbo、springcloud)实现服务之间的通信,而这些RPC框架默认使用的协议则是基于Google的Protocol Buffers(简称Protobuf)。这里我将介绍Java中如何使用protobuf-java框架生成proto文件及相应的Java类并序列化/反序列化消息数据。如果你对Protobuf不了解,可以先阅读相关介绍材料再继续阅读本文。

Protobuf简介

Protocol Buffer (Protobuf) 是 Google 于2008年发布的高性能、灵活的结构化数据标准。它主要用于方便地描述结构化的数据,可用于跨平台快速通讯,更适合用于大规模互联网系统的数据交换。相比 XML、JSON等非结构化数据编码方式,Protobuf 的编解码效率要优于它们。Google内部也在逐渐使用 Protobuf 来替代其内部的传输协议,如gRPC。对于开发者来说,Protobuf 提供了一系列便利的工具,包括protoc编译器、预定义消息类型以及各种语言的库支持。你可以参考官方网站的教程来学习更多关于 Protobuf 的知识。

Protobuf的安装配置

安装环境准备

如果你还没有安装过protobuf的相关工具或环境,那么首先需要准备好以下的软件环境:
1.下载安装包
2.安装Java环境(这里假设你已经安装了Java环境,如果没有安装,可以参考我的另一篇文章《Java环境搭建指南》进行安装)
3.设置环境变量
4.安装protobuf运行环境
如果你的机器上已经有相关的软件环境,你可以跳过此步。
1.下载protoc编译器压缩包,https://github.com/protocolbuffers/protobuf/releases,本例采用v3.9.1版本
2.下载完成后解压,进入bin目录,执行protoc.exe命令查看是否正确安装。正常情况下应该会打印出类似如下信息:

protoc 3.9.1
Usage: protoc [OPTION]... FILES
Parse PROTO_FILES and generate output based on options given:
...
Please specify a command with --help for more information.

如果能够看到Usage的信息说明安装成功。
3.设置系统环境变量,找到“计算机”–>“属性”–>“高级系统设置”–>“环境变量”菜单,在“用户变量”中点击“新建”,然后输入PROTOBUF_HOME,值为protoc所在目录。然后在“系统变量”中找到PATH,点击编辑,在后面添加%PROTOBUF_HOME%\bin,示例如下图所示。

4.最后重新打开CMD或者其他终端窗口,验证是否生效。

protoc --version

正常情况下输出类似:

libprotoc 3.9.1

编写Protobuf文件

使用Protobuf消息格式主要涉及到两个方面:定义消息格式和使用消息格式。定义消息格式是在.proto 文件中声明消息类型,例如声明一个Person类型的消息格式,其中包含name字符串字段、id整型字段和email字符串数组字段,可以按照如下方式定义。

syntax = "proto3"; //指定protobuf版本,语法最低版本为proto3
package demo; //指定当前文件的命名空间,通常建议按功能模块划分不同的命名空间

message Person {
  string name = 1;
  int32 id = 2;
  repeated string email = 3;
}

注意

  • syntax:该选项用于指定该.proto文件的语法版本,proto3即是最新版本,也是本文使用的语法版本;
  • package:该选项用于指定当前文件的命名空间,命名空间一般建议按功能模块划分不同的命名空间,避免不同项目间发生冲突。

通过 .proto 文件可以生成对应语言的代码,因此第一步需要确认自己正在使用的编程语言,本文使用Java作为演示语言。

使用Protobuf编译器编译生成Java类

使用Protobuf消息格式定义完毕后,就可以用Protobuf编译器生成对应的Java类了,生成Java类的过程实际是编译.proto文件生成一个.java文件,所以需要确保你已经安装了Protoc编译器。

编译命令如下:

$ protoc -I= --java_out= 

参数说明:

-I: 指定导入的.proto文件的查找路径,可以在多个路径中用冒号分割,但是推荐只设置一个路径,否则容易导致生成的文件名重复;

--java_out: 指定生成Java类的存放目录;

: 指定要编译的.proto文件列表,可以用星号*表示匹配所有.proto文件,也可以用相对或绝对路径指定单个文件。

例如:

$ protoc -I=. --java_out=../main/java person.proto

上面命令表示将person.proto文件编译成Java类并存放在../main/java文件夹下。编译完成后,可以查看当前目录下的生成文件,其中就包含刚才生成的Person类。

在Java中使用Protobuf

生成的Java类非常类似于原始的.proto文件中的消息定义,并且提供了相应的方法来序列化和反序列化消息数据。比如说,可以通过如下代码构造一个Person对象,并对其序列化:

import com.google.protobuf.*;

public class Main {
  public static void main(String[] args) throws Exception {
    Person person = Person.newBuilder()
     .setName("Alice")
     .setId(123)
     .addEmail("")
     .addEmail("")
     .build();

    byte[] data = person.toByteArray();
    
    FileOutputStream fos = new FileOutputStream("test.dat");
    fos.write(data);
    fos.close();
  }
}

上面代码构造了一个新的Person对象,并设置了三个字段的值,然后调用toByteArray()方法将其序列化为字节数组。接着保存到本地文件test.dat

读取已有的字节序列,恢复到Person对象,可以使用如下代码:

FileInputStream fis = new FileInputStream("test.dat");
byte[] data = new byte[fis.available()];
fis.read(data);
fis.close();

Person person = Person.parseFrom(data);

System.out.println("Name: " + person.getName());
System.out.println("ID: " + person.getId());
System.out.println("Emails:");
for (String email : person.getEmailList()) {
  System.out.println("- " + email);
}

上面代码读取字节序列data,恢复成Person对象,然后打印各字段的值。parseForm()方法是从字节序列解析出Person对象。

总结

本文介绍了如何在Java中使用Protobuf编译器来生成.proto文件,并通过Java API对消息进行序列化和反序列化。Protobuf是一个强大的序列化协议,建议使用它的原因很多,包括跨平台性、高性能、简单易用等,大家可以根据自己的需求选择不同的方案。

你可能感兴趣的:(java,开发语言)