作者:禅与计算机程序设计艺术
最近随着微服务架构的流行,越来越多的人选择了基于RPC框架(比如dubbo、springcloud)实现服务之间的通信,而这些RPC框架默认使用的协议则是基于Google的Protocol Buffers(简称Protobuf)。这里我将介绍Java中如何使用protobuf-java框架生成proto文件及相应的Java类并序列化/反序列化消息数据。如果你对Protobuf不了解,可以先阅读相关介绍材料再继续阅读本文。
Protocol Buffer (Protobuf) 是 Google 于2008年发布的高性能、灵活的结构化数据标准。它主要用于方便地描述结构化的数据,可用于跨平台快速通讯,更适合用于大规模互联网系统的数据交换。相比 XML、JSON等非结构化数据编码方式,Protobuf 的编解码效率要优于它们。Google内部也在逐渐使用 Protobuf 来替代其内部的传输协议,如gRPC。对于开发者来说,Protobuf 提供了一系列便利的工具,包括protoc编译器、预定义消息类型以及各种语言的库支持。你可以参考官方网站的教程来学习更多关于 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消息格式主要涉及到两个方面:定义消息格式和使用消息格式。定义消息格式是在.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消息格式定义完毕后,就可以用Protobuf编译器生成对应的Java类了,生成Java类的过程实际是编译.proto
文件生成一个.java
文件,所以需要确保你已经安装了Protoc编译器。
编译命令如下:
$ protoc -I=
参数说明:
-I
: 指定导入的.proto
文件的查找路径,可以在多个路径中用冒号分割,但是推荐只设置一个路径,否则容易导致生成的文件名重复;
--java_out
: 指定生成Java类的存放目录;
: 指定要编译的.proto
文件列表,可以用星号*
表示匹配所有.proto
文件,也可以用相对或绝对路径指定单个文件。
例如:
$ protoc -I=. --java_out=../main/java person.proto
上面命令表示将person.proto
文件编译成Java类并存放在../main/java
文件夹下。编译完成后,可以查看当前目录下的生成文件,其中就包含刚才生成的Person
类。
生成的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是一个强大的序列化协议,建议使用它的原因很多,包括跨平台性、高性能、简单易用等,大家可以根据自己的需求选择不同的方案。