一、简介
官网:https://developers.google.com/protocol-buffers/ 或者 https://developers.google.cn/protocol-buffers
托管gitbub地址:https://github.com/protocolbuffers/protobuf
介绍:是一种语言中立、平台中立、可扩展的机制,用于序列号结构化数据。比如如果用http传输,传输的比如是json或xml,效率低,而使用protobuf可以将数据压缩后传输;如果使用java系列化,则无法实现与非java语言交换数据,而使用protobuf可以实现与其他语言交换数据。
Rpc支持的语言越多,提供的数据类型越少,因为数据类型一定是所有支持语言数据类型的交集。
protobuf支持语言:C++ C# Dart Go Java Python
二、使用
1、搭建protobuf开发环境
1)下载protobuf编译器
protoc开头的才是编译器
2)解压后放在任意路径,并将bin路径配置到环境变量path下(windows),linux修改对应的环境遍历配置文件
3)下载针对特定语言的protobuf文件,这里下载JAVA的
这个压缩包其实是特定语言涉及的protocolbuf源码、依赖,如果项目使用gradle或者maven管理的话,此步可以省略
4)在工程中引入开发protobuf程序依赖包
Protobuf runtime installation,是从这个链接进来
Maven依赖:
protobuf-java:开发protobuf程序依赖的核心包
protobuf-java-util:开发protobuf辅助类、比如json支持
Gradle依赖:
2、开发protobuf程序
示例来源:https://developers.google.cn/protocol-buffers/docs/javatutorial
2.1定义将要传递的消息格式
1)分析消息中属性
2)编写.proto文件
- package:protobuf中的包(.proto文件的起始标记)
- java_package:java中的包,如果package与java_package同时有,生成java代码中的包路径则以java_package为准,如果没有java_package,生成java代码中的包路径则以package为准,package是必须的属性用于其他没有包概念的语言区分命名空间用的,java_package是可选的属性(专门用于java语言的)最佳实践:如果IDL是准备给java语言用,则java_package不要省略,因为通常package的值不是按照java中规则设置的(com.mzj.xxx)
- java_outer_classname:是用于定义生成的java源码文件最外层的java文件类名(protobuf生成的源码默认只有一个类文件,IDL中定义的message作为这个类的内部类),这个文件包含了所有下面定义的java类,也就是下面定义的java类,都会生成在这个外层类内部,称为这个类的内部类;如果不设置这个选项,默认采用IDL文件名转换驼峰的方式生成。最佳实践:如果IDL是准备给java语言用,则java_outer_classname不要省略
- protobuf的IDL主体,是以一个个message(消息)组成,一个message代表一个对象类型
- Protobuf的IDL支持类型:bool、int32、float、double、string
- 可以自定义类型,如Person类型
- 可以自定义类型嵌套,如:PhoneNumber是Person的内部类型
- 可以定义枚举类型,例子中enum是枚举类型
- 字段名称protobuf推荐使用_分割字母,因为为了适应多语言的要求,而java语言时,会在编译后自动转成驼峰形式
- =1,=2,=3:是唯一标识字段的(一个消息内唯一,不能重复)标记1-15用于常用的字段,16以后用于不常用字段(因为15以内的标记,占用字节少,可以减少少许传输大小)。注意不同message中标识也不能重复
- 字段类型标记:①required必须的,使用不当可能抛出RuntimeException、IOException,②optional可选的,如果不指定,则采用默认值,设置optional字段默认值方式:
如果不指定optional的默认值,则采用系统提供的默认值③repeated可重复的(实际上是java中ArrayList)
- 如:repeated string names;表示names是一个集合类型,集合中元素是string类型
- 注意:设置字段是required是需要小心的,如果一开始设置成required,以后想要改成optional是不可以的(会产生兼容问题)。最佳实践:不要使用required,而使用optional、repeated,因为required的扩展性、兼容性不强。
- proto的IDL不支持继承(protobuf不支持继承)
2.2编译IDL文件(即.proto文件)
编译生成的类文件说明:
- 最外层:AddressBookProtos.java
- java文件里针对每一个消息(message)都生成了一个内部class,每一个类内部也都有个Builder类,可以用这个Builder类型创建对应message的实例(构造器设计模式)
- 消息只有get方法,构造器即有get,也有set方法
- 消息message:
- 构造器Builder
- 由protobuf创建的message类是不可修改的类。
- 创建消息对象,并设值,是通过创建消息对应的Builder对象并调用Builder对象的setXXX进行设值,最后通过build方法创建message:(一句话概述:通过构造器模式以方法链方式创建message)
- 消息的标准通用方法
生成的消息类有一些通用方法供编程中调用:
- 消息的解析与序列化方法
消息对象构造完成或者获取到消息流后,对消息进行序列化与反序列化
不只是只有上面官网给出的这些方法,还有其他方法:
- 消息不能通过继承进行扩展,如果想扩展,只能通过组合方式
- protoc生成的java文件永远不要试图修改,因为如果重新生成,修改就没有了
最佳实践:不要修改protoc生成的java文件
- 实际开发时更多的细节参考protobuf的language guide
https://developers.google.cn/protocol-buffers/docs/proto
完整程序示例: