赞
踩
以一个简单例子对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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。