以一个简单例子对pb进行展开讲解
syntax = "proto3";
package rpc.rpcclient.rpccall;
option java_package="com.ppp.mpj";
option java_multiple_files = false;
option java_outer_classname = "MpjBean";
// 建议使用谷歌protobuf规范 遵循PB语法检查
// 谷歌protobuf规范地址:https://developers.google.com/protocol-buffers/docs/style
// 不建议使用 google/protobuf/any.proto
// any强依赖package type.googleapis.com/_packagename_._messagename_.
// https://developers.google.com/protocol-buffers/docs/proto3#any
// 在Proto中开启数据校验不仅可减少代码编写量,还能预防约75%的安全风险
import "rpc/common/validate.proto";
service MinOlapSql {
//根据班级信息,返回对应的体重sum结果信息
rpc getStudentWeightSum(ClazzReq) returns (AuditRes);
//根据班级信息,返回对应的体重count结果信息
rpc getStudentWeightCount(ClazzReq) returns (AuditRes);
//根据班级信息以及年龄阈值信息,返回对应的体重count结果信息
rpc getStudentWeightSumWithThreshold(ClazzWithThresholdReq) returns (AuditRes);
}
enum ClassType {
ALL = 0;
BLADE = 1;
PRESTO = 2;
}
message StudentReq {
int32 sid = 1;
string name = 2;
double weight = 3;
int32 age = 4;
ClassType classid = 5;
}
message ClazzWithThresholdReq {
ClazzReq req = 1;
int32 agethreshold = 2;
}
message ClazzReq {
ClassType clazzid = 1; // 班级id
string clazzname = 2; // 班级名
}
message AuditRes {
int32 ret = 1; // 返回码
double res = 2; // 返回信息
repeated StudentReq stulist = 3;
}
1、第一行的syntax指定了正在使用proto3语法,假如不指定,默认使用proto2,这必须是文件的第一个非空、非注释行。
2、Package可以避免协议消息类型键的名称冲突,之后你可以在定义消息时指定是哪个包下的字段,比如
message Foo { ... foo.bar.Open open = 1; ... }
Package在c++中对应namespace。而对于Java,包声明符会变为java的一个包(实测,在bean中全为备注),在.proto文件中可以提供一个java_package。这里就明确指定了java_package,即生成的包路径。
Protocol buffer语言中的类型名称解析类似于C++:首先在最内层查找,之后是下一层,一次类推,每个包在其父包的“内部”。“.”开头(例如,.foo.bar.Baz
)意味着从最外层作用域开始查找。
Protocol buffer编译器通过导入的.proto
文件来解析所有的类型名称。即使有着不同的作用域规则,各语言生成的代码也知道如何每种类型该如何使用。
3、import "address.proto"; //引入其他的proto文件,如果是其他文件夹的文件也不需要加路径,
只需要在生成代码时,选择proto文件的扫描范围时,需要将所有导入proro文件都包含在内。
通过--proto_path属性指定。
4、package com.study.blog.protobuf; // proto文件的命名空间
5、option java_package = "com.study.blog.protobuf"; //生成代码后的命令空间,不写默认就是proto的package
6、option java_outer_classname="PersonProto"; //这个名字不能与下文的message名字冲突,Person已经是一个message类型,就不能作为ClassName。默认是proto的文件名作为className。
7、option java_multiple_files = true; //如果为true,每个message和service都会被生成为一个类。如果是false,则所有的message和service都将会是java_outer_classname的内部类。默认为false,生成一个文件。
8、option java_generic_services = true; //是否生成Service,如果这个属性为false,
将不会生成Service的代码,即使已经在proto文件编写了service结构(proto2中的属性)
9、option optimize_for = SPEED;//对生成的代码的一种优化,有三个值:SPEED, CODE_SIZE, LITE_RUNTIME; 表示希望生成代码是偏向执行速度,还是生成的文件大小,如果在app端,代码文件的大小是很重要的。默认为SPEED。
10、deprecated表示该字段是否被弃用。
int32 old_field = 6 [deprecated = true];
11、 int32 sid = 1;
如上所见,消息定义中每个字段都有一个唯一的编号,这些
protobuf属性 | C++属性 | java属性 | 备注 |
double | double | double | 固定8个字节 |
float | float | float | 固定4个字节 |
int32 | int32 | int32 | 使用变长编码,对于负数编码效率较低,如果经常使用负数,建议使用sint32 |
int64 | int64 | int64 | 使用变长编码,对于负数编码效率较低,如果经常使用负数,建议使用sint64 |
uint32 | uint32 | int | 使用变长编码。(只可以表示正数) |
uint64 | uint64 | long | 使用变长编码。(只可以表示正数) |
sint32 | int32 | int | 采用zigzag压缩,对负数编码效率比int32高 |
sint64 | int64 | long | 采用zigzag压缩,对负数编码效率比int64高 |
fixed32 | uint32 | int | 总是4字节,如果数据>2^28,编码效率高于unit32 |
fixed64 | uint64 | long | 总是8字节,如果数据>2^56,编码效率高于unit32 |
sfixed32 | int32 | int | 总是4字节 |
sfixed64 | int64 | long | 总是8字节 |
bool | bool | boolean | |
string | string | String | 一个字符串必须是utf-8编码或者7-bit的ascii编码的文本 |
bytes | string | ByteString | 可能包含任意顺序的字节数据 |
代码生成步骤:
1、下载proto编译器,下载链接: windows版本, linux-64
2、下载后,将其进行解压。然后配置path环境变量,环境变量指向{加压路径}/bin
3、使用如下命令生成代码:
protoc --proto_path=src/main/proto --proto_path=src/main/protocbuf
--java_out=src/main/java src/main/proto/person.proto src/main/proto/address.proto
说明:
--proto_path,用于指定proto文件的扫描范围,可以指定多次
--java_out,后面接了三个路径;第一个路径是生成代码的存放路径,第二个和第三个路径是需要生成代码的proto文件
未完待续
官方文档 https://developers.google.com/protocol-buffers/docs/proto3