介绍
Protobuf是一种轻便高效的结构化数据存储格式,平台无关、语言无关、可扩展,可用于通讯协议和数据存储等领域。
优点
- 平台无关,语言无关,可扩展;
- 提供了友好的动态库,使用简单;
- 解析速度快,比对应的XML快约20-100倍;
- 序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10。
编译
安装 vs
https://blog.csdn.net/yuan_lo/article/details/67632128
安装 cmake
编译安装
源码下载地址 https://github.com/google/protobuf
Windows还要下载protobuf-2.5.0-windows.zip。解压并把protoc.exe放在Protobuf安装目录下的src里。
yum install -y autoconf automake libtool curl make g++ unzip 安装依赖的库
./autogen.sh
./configure
make
make check
make install
实例
编写 addressbook.proto
syntax="proto2"; // 表明使用protobuf的编译器版本为v2,目前最新的版本为v3
package addressbook; // 声明了一个包名,用来防止不同的消息类型命名冲突,类似于 namespace
option java_package = "com.jado.ljh.protobuf";
option java_outer_classname = "HeartBeat";
// import "src/help.proto"; // 导入了一个外部proto文件中的定义,类似于C++中的 include 。不过好像只能import当前目录及当前目录的子目录中的proto文件。
// required:该值是必须要设置的;
// optional :该字段可以有0个或1个值(不超过1个);
// repeated:该字段可以重复任意多次(包括0次),类似于C++中的list;
message Person { //结构化数据,类似于C++中的类
required string name = 1; // 声明了一个名为name,数据类型为string的required字段,字段的标识号为1
required int32 id = 2;
optional string email = 3;
enum PhoneType { // 类似于C++中的类内声明,Person外部的结构可以用 Person.PhoneType 的方式来使用PhoneType。
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message AddressBook {
repeated Person person_info = 1;
}
编译成java类
protoc.exe -I=D:/project/git/spring_boot/protobuf --java_out=d:/tmp D:/project/git\spring_boot/protobuf/src/main/resources/com/hry/spring/proto/simple/*.proto
# protoc.exe -I=proto的输入目录 --java_out=java类输出目录 proto的输入目录包括包括proto文件
测试
// 模拟将对象转成byte[],方便传输
PersonEntity.Person.Builder builder = PersonEntity.Person.newBuilder();
builder.setId(1);
builder.setName("ant");
builder.setEmail("ghb@soecode.com");
PersonEntity.Person person = builder.build();
System.out.println("before :"+ person.toString());
System.out.println("===========Person Byte==========");
for(byte b : person.toByteArray()){
System.out.print(b);
}
System.out.println();
System.out.println(person.toByteString());
System.out.println("================================");
//模拟接收Byte[],反序列化成Person类
byte[] byteArray =person.toByteArray();
Person p2 = Person.parseFrom(byteArray);
System.out.println("after :" +p2.toString());
protobuf与maven集成
难题
- 多平台protoc 团队中有人使用mac 有人使用windows开发,编译使用的protoc是二进制程序,根据不同操作系统有不同的版本。
- 按需编译 有一些模块(module)包含有proto目录,需要编译其中的.proto文件。另一些模块不包含proto目录,不需要编译。
- 跨模块引用 有一些模块需要引用到其他模块的proto目录。
全局属性
<protobuf.input.directory>${project.basedir}/src/main/proto</protobuf.input.directory>
<protobuf.output.directory>src/main/gen-java</protobuf.output.directory>
<protobuf.protoc.path>${settings.localRepository}/protoc</protobuf.protoc.path>
<protobuf.version>3.2.0</protobuf.version>
引用protobuf-API:编译成的.java文件需要依赖这些库文件
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
识别系统类型:区分Windows/Linux/OS X 不同系统,区分32/64-bit,识别结果会写入到变量里面
<build>
<extensions>
<!-- provides os.detected.classifier (i.e. linux-x86_64, osx-x86_64) property -->
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.4.1.Final</version>
</extension>
</extensions>
</build>
自动下载protoc到本地目录
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy-protoc</id>
<phase>generate-sources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.google.protobuf</groupId>
<artifactId>protoc</artifactId>
<version>${protobuf.version}</version>
<classifier>${os.detected.classifier}</classifier>
<type>exe</type>
<overWrite>false</overWrite>
<outputDirectory>${protobuf.protoc.path}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
编译.proto文件
<propertyregex property="proto.include"
input="${protobuf.input.directory}"
regexp=";"
replace=" -I"
defaultvalue="${protobuf.input.directory}"
global="true" />
最终呈现的插件配置
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<dependencies>
<dependency>
<groupId>ant-contrib</groupId>
<artifactId>ant-contrib</artifactId>
<version>1.0b3</version>
<exclusions>
<exclusion>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-apache-regexp</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-nodeps</artifactId>
<version>1.8.1</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>compile-protoc</id>
<phase>generate-sources</phase>
<configuration>
<tasks>
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
<available property="isExist" file="src/main/proto" type="dir"/>
<if>
<equals arg1="${isExist}" arg2="true"/>
<then>
<property name="protoc.filename"
value="protoc-${protobuf.version}-${os.detected.classifier}.exe"/>
<property name="protoc.filepath"
value="${protobuf.protoc.path}/${protoc.filename}"/>
<propertyregex property="proto.include"
input="${protobuf.input.directory}"
regexp=";"
replace=" -I"
defaultvalue="${protobuf.input.directory}"
global="true" />
<chmod file="${protoc.filepath}" perm="ugo+rx"/>
<path id="proto.path">
<fileset dir="src/main/proto">
<include name="**/*.proto"/>
</fileset>
</path>
<mkdir dir="src/main/gen-java"/>
<pathconvert pathsep=" " property="proto.files" refid="proto.path"/>
<exec executable="${protoc.filepath}" failonerror="true">
<arg value="--java_out=${protobuf.output.directory}"/>
<arg value="-I${proto.include}"/>
<arg line="${proto.files}"/>
</exec>
</then>
</if>
</tasks>
<sourceRoot>${protobuf.output.directory}</sourceRoot>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
注意
- 使用maven-antrun的if语法,可以判断目录是否存在。如果目录存在,才开始编译工作。
- 在命令行调用protoc文件的时候,可以加入多个-I参数引用多个路径。
- 这里我们利用maven-antrun的propertyregex 工具,进行字符串替换。需要跨模块引用时,只需覆写protobuf.input.directory属性,多个目录之间用分号间隔。
- 插件依赖: if语法需要依赖插件ant-contrib:ant-contrib
- 插件依赖: propertyregex工具需要引用 org.apache.ant:ant-apache-regexp
- propertyregex 在匹配失败(regexp一个都没有匹配到)的时候,不会设置property;因此需要加入defaultvalue 设置成源串。