菜狗教程 —— Protobuf 篇 01
官方文档:protobuf.dev/
Protobuf 的概念和用处
Protobuf 的意义是一件比较混乱的事,在日常开发的沟通里当咱们说到 Protobuf 时,很或许还要依据前后语境判别详细指代什么东西,写出来的时分丢失了语境,所以咱们要一致一个界说。
依照官方文档的界说:
Protocol Buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data.
首先,Protobuf 是一个机制(mechanism),而不是一种数据(data)。这意味着 Protobuf 在运用的过程中包含多个步骤,其间包括了序列化成数据和其他的步骤。
其次,Protobuf 具有言语中立、渠道中立和可扩展的特性,所以能够用于简直所有的场景,无论是一个体系内不同编程言语之间的交互,仍是网络恳求这种一同跨言语跨渠道的场景都没问题。
最后我想弥补一点界说中短少的特性,Protobuf 机制序列化之后的数据是二进制的,算是有一层天然的加密,而且一般情况下会比 JSON 序列化成字符串数据量更小,小就表明更省流量和贮存空间,网络传输耗时也更短,是很大的优势。
凡事皆有价值,Protobuf 许多优势背面的价值是较为杂乱的运用方式,先看一个流程图:
- 界说
*.proto
文件 - 运用
protoc
编译成不同言语的代码 - (运用详细某种言语)创立数据并序列化成二进制文件(或数据流)
- (运用详细某种言语)读取二进制文件,解析出其间的内容
从 Protobuf 的优点和运用方式来看,最适合 Protobuf 的运用场景就是网络恳求和本地贮存比较杂乱的结构化数据,这两种在客户端开发中都很常见。别的,在 Android App 开发中,由于 JNI 的杂乱度比较高,用 Protobuf 在 Kotlin 和 C++ 之间做数据交互也是能下降开发难度的。
Protobuf 的装置和编译
依据运用流程,Protobuf 需求先界说 .proto 文件,再编译成咱们需求的言语的代码,编译运用的工具是 Protobuf 提供的 protoc
。
装置的说明在 github.com/protocolbuf…
假如不是特别需求,引荐运用预编译好的二进制包,解压之后就能用,便利操控 protoc
的版别,假如涉及多个项目的开发,乃至或许需求一同运用多个版别的 protoc
。
protoc
的用法规则是:
Usage: protoc [OPTION] PROTO_FILES
Options:
### 输出当时 protoc 版别 ###
--version Show version info and exit.
### 指定输入文件途径 ###
-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.
### 运用插件 ###
--plugin=EXECUTABLE
### 指定输出文件途径 ###
--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.
--js_out=OUT_DIR Generate JavaScript source.
--objc_out=OUT_DIR Generate Objective C header and source.
--php_out=OUT_DIR Generate PHP source file.
--python_out=OUT_DIR Generate Python source file.
--ruby_out=OUT_DIR Generate Ruby source file.
此外还有一些可选参数,能够通过 protoc -h
查看。
Protobuf 的界说和运用
介绍完编译方式,是时分来实践一下了。
假定一个场景,咱们需求把一个单词的数据从 Kotlin 传给 C++。依照运用流程,咱们先界说数据结构,新建一个 word.proto 文件。
syntax="proto3";
package cg;
option java_package = "com.caigou.example.proto";
option java_outer_classname = "WordProto";
message Word {
int32 id = 1;
string spell = 2;
string mean = 3;
}
然后运用 protoc 来编译这段代码,分别生成对应的 C++ 类和 Java 类。
#/path/to/cpp/proto
$ protoc -I ./ --cpp_out=./ word.proto
#/path/to/java/proto
$ protoc -I ./ --java_out=./ word.proto
正确的执行在终端不会输出任何内容。
C++ 的输出是一个标准的 class,内容适当丰富,用上面短短几行 proto 编译出来的两个文件加起来有接近 800 行代码,此处就不贴出来了。
Java 的输出咱们增加了一点 option 操控了包名和文件名,默许的包名跟 proto 里边的 package 相同,一般都不契合 Java 的编程标准。
然后就能够在 Kotlin 代码中创立一个 Word 目标并序列化成文件,交给 C++ 读取。
Tips: 此处仅展现功能,省去了比较费事的 JNI,两边是两个独立的可执行程序。程序中需求装备一些 protobuf 的依靠才干正常运转,此处不做赘述。
import com.caigou.example.proto.WordProto
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
fun main(args: Array<String>) {
val word = WordProto.Word.newBuilder()
.setId(10000)
.setSpell("abandon")
.setMean("扔掉,放弃")
.build()
write2File(word.toByteArray(), File("./pb_word"))
}
private fun write2File(byteArray: ByteArray, outputFile: File) {
try {
val fos = FileOutputStream(outputFile, false)
fos.write(byteArray)
fos.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
代码很简略,就是把最经典的第一个单词写入 pb_word 文件。得到文件之后仿制到 C++ 的工程中,再写一段读取数据的代码:
#include <iostream>
#include <string>
#include <fstream>
#include "proto/word.pb.h"
int main() {
std::cout << "Start Read File" << std::endl;
std::string data;
std::ifstream fin("./pb_word", std::ios::binary | std::ios::in);
fin >> data;
fin.close();
auto word = cg::Word();
word.ParseFromArray(data.data(), data.size());
std::cout << word.Utf8DebugString() << std::endl;
return 0;
}
Let’s go!
到这里你现已完全学会运用 Protobuf 了,快去试试吧!
彩蛋环节
ip 被限流了,么得 ChatGPT 玩了,自问自答两个问题吧。
Q1. 明明是 Kotlin 的 sample,为什么不直接编译成 kt 文件呢?
A1:现在的确支撑 –kotlin_out 了,仅仅因为工作中运用的环境是用 Java 写了 JNI,然后 Protobuf 生成的代码也在一同,用 Java 比较一致。别的 Kotlin 相关文档也不是很完善,不敢轻率提出替换。结构一个 Message 目标用 Kotlin 肯定会容易许多,持续重视。
Q2. 仿制了你这代码运转不起来啊?
A2:的确,文中也说到了工程中运用 Protobuf 需求额定的依靠,依靠就存在版别问题,还有跟 protoc
的兼容问题,都写出来似乎有点主次不分了。实际工作中 Protobuf 基本上是两个不同端的同事一同运用,我没想到有什么特别简略直接的单个工程能作为示例的,有想法能够教教我(真挚)。