简介
Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。
Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。
如果你在开发中使用过常用的数据交换格式如xml、json,那么protocol buffer也不是什么神奇的东西了,它和xml、json类似,也可以作为开发中的一种数据交换格式,只不过相较xml和json,protocol buffer的优点更明显,它更小、更快、更简单。
windows下使用Protobuf
对比非C++用户,可以直接下载官网预编译好的protoc.exe(install the protocol compiler is to download a pre-built binary):
https://repo1.maven.org/maven2/com/google/protobuf/protoc/
Java中使用ProtocolBuffer
在Java中使用ProtocolBuffer的步骤大致分为下面这几点:
- (1)编写.proto文件,定义消息类型
application.proto
syntax = "proto2";
package proto;
option java_package = "com.ziyun.bean.proto";
option java_outer_classname = "IpAddress";
message ip_address {
optional string af = 1;
optional string addr = 2;
}
(2)使用ProtocolBuffer的编译器(protoc.exe),将.proto文件编译成对应的java文件
public class GenereteBeanUtil { /** * 通过执行cmd命令调用protoc.exe程序 * 参考命令:protoc2.exe -I=./ --java_out=./ ./proto/access_point.proto * * @param absolutePath exe程序绝对路径 * @param protoFileName proto文件名 */ public static void generateBean(String absolutePath, String protoFileName) { String[] cmd = {absolutePath + "protoc2.exe", "-I=" + absolutePath, "--java_out=" + absolutePath, absolutePath + "proto/" + protoFileName}; try { Runtime.getRuntime().exec(cmd); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { File directory = new File(""); String absolutePath = directory.getAbsolutePath(); //proto文件路径 String protoPath = absolutePath + File.separator + "zy-libs" + File.separator + "zy-zeromq" + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator + "proto" + File.separator; String dirPath = absolutePath + File.separator + "zy-libs" + File.separator + "zy-zeromq" + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator; File dir = new File(protoPath); File[] files = dir.listFiles(); for (File file : files) { String fileName = file.getName(); GenereteBeanUtil.generateBean(dirPath, fileName); } } }
(3)在Java代码中使用上一步编译好的java文件
```
IpAddress.ip_address.Builder builder = IpAddress.ip_address.newBuilder();
builder.setAddr("10.130.254.6");
builder.setAf("af" + String.valueOf(i));
IpAddress.ip_address ipAddress = builder.build();
publisher.send(ipAddress.toByteArray(), ZMQ.NOBLOCK);
## 语法
### 类型对比
![image](E:/image/prototype.png)
### package
.proto文件新增一个可选的package声明符,用来防止不同的消息类型有命名冲突。包的声明符会根据使用语言的不同影响生成的代码。对于C++,产生的类会被包装在C++的命名空间中。
### 枚举(enum)
enum PhoneType //枚举消息类型
{
MOBILE = 0; //proto3版本中,首成员必须为0,成员不应有相同的值
HOME = 1;
WORK = 2;
}
### 指定字段规则
所指定的消息字段修饰符必须是如下之一:
- required:一个格式良好的消息一定要含有1个这种字段。表示该值是必须要设置的;
- optional:消息格式中该字段可以有0个或1个值(不超过1个)。
- repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于java中的List。
由于一些历史原因,基本数值类型的repeated的字段并没有被尽可能地高效编码。在新的代码中,用户应该使用特殊选项[packed=true]来保证更高效的编码。如:
repeated int32 samples = 4 [packed=true];
### import
import "myproject/other_protos.proto";
protocol编译器就会在一系列目录中查找需要被导入的文件,这些目录通过protocol编译器的命令行参数-I/–import_path指定。如果不提供参数,编译器就在其调用目录下查找。
### protoc命令参数
protoc2.exe -I=./ --java_out=./ ./proto/access_point.proto
$ protoc -I=./src --python_out=./out ./src/*.proto //用protoc3试试
$ protoc -help
Usage: protoc [OPTION] PROTO_FILES
Parse PROTO_FILES and generate output based on the options given:
-IPATH, --proto_path=PATH Specify the directory in which to search for
imports. May be specified multiple times;
directories will be searched in order. If not
given, the current working directory is used.
--version Show version info and exit.
-h, --help Show this text and exit.
--encode=MESSAGE_TYPE Read a text-format message of the given type
from standard input and write it in binary
to standard output. The message type must
be defined in PROTO_FILES or their imports.
--decode=MESSAGE_TYPE Read a binary message of the given type from
standard input and write it in text format
to standard output. The message type must
be defined in PROTO_FILES or their imports.
--decode_raw Read an arbitrary protocol message from
standard input and write the raw tag/value
pairs in text format to standard output. No
PROTO_FILES should be given when using this
flag.
-oFILE, Writes a FileDescriptorSet (a protocol buffer,
--descriptor_set_out=FILE defined in descriptor.proto) containing all of
the input files to FILE.
--include_imports When using --descriptor_set_out, also include
all dependencies of the input files in the
set, so that the set is self-contained.
--include_source_info When using --descriptor_set_out, do not strip
SourceCodeInfo from the FileDescriptorProto.
This results in vastly larger descriptors that
include information about the original
location of each decl in the source file as
well as surrounding comments.
--dependency_out=FILE Write a dependency output file in the format
expected by make. This writes the transitive
set of input file paths to FILE
--error_format=FORMAT Set the format in which to print errors.
FORMAT may be ‘gcc‘ (the default) or ‘msvs‘
(Microsoft Visual Studio format).
--print_free_field_numbers Print the free field numbers of the messages
defined in the given proto files. Groups share
the same field number space with the parent
message. Extension ranges are counted as
occupied fields numbers.
--plugin=EXECUTABLE Specifies a plugin executable to use.
Normally, protoc searches the PATH for
plugins, but you may specify additional
executables not in the path using this flag.
Additionally, EXECUTABLE may be of the form
NAME=PATH, in which case the given plugin name
is mapped to the given executable even if
the executable‘s own name differs.
--cpp_out=OUT_DIR Generate C++ header and source.
--csharp_out=OUT_DIR Generate C# source file.
--java_out=OUT_DIR Generate Java source file.
--javanano_out=OUT_DIR Generate Java Nano source file.
--js_out=OUT_DIR Generate JavaScript source.
--objc_out=OUT_DIR Generate Objective C header and source.
--python_out=OUT_DIR Generate Python source file.
--ruby_out=OUT_DIR Generate Ruby source file.
```
Protobuf 的不足
Protbuf 与 XML 相比也有不足之处。它功能简单,无法用来表示复杂的概念。
XML 已经成为多种行业标准的编写工具,Protobuf 只是 Google 公司内部使用的工具,在通用性上还差很多。
由于文本并不适合用来描述数据结构,所以 Protobuf 也不适合用来对基于文本的标记文档(如 HTML)建模。另外,由于 XML 具有某种程度上的自解释性,它可以被人直接读取编辑,在这一点上 Protobuf 不行,它以二进制的方式存储,除非你有 .proto 定义,否则你没法直接读出 Protobuf 的任何内容【 2 】。
参考文献
tips:本文属于自己学习和实践过程的记录,很多图和文字都粘贴自网上文章,没有注明引用请包涵!如有任何问题请留言或邮件通知,我会及时回复。