菜狗教程 —— 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 许多优势背面的价值是较为杂乱的运用方式,先看一个流程图:

【菜狗教程】Protobuf.01 - 基本用法和特性介绍

  1. 界说 *.proto 文件
  2. 运用 protoc 编译成不同言语的代码
  3. (运用详细某种言语)创立数据并序列化成二进制文件(或数据流)
  4. (运用详细某种言语)读取二进制文件,解析出其间的内容

从 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

正确的执行在终端不会输出任何内容。

【菜狗教程】Protobuf.01 - 基本用法和特性介绍

C++ 的输出是一个标准的 class,内容适当丰富,用上面短短几行 proto 编译出来的两个文件加起来有接近 800 行代码,此处就不贴出来了。

Java 的输出咱们增加了一点 option 操控了包名和文件名,默许的包名跟 proto 里边的 package 相同,一般都不契合 Java 的编程标准。

【菜狗教程】Protobuf.01 - 基本用法和特性介绍

然后就能够在 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.01 - 基本用法和特性介绍

到这里你现已完全学会运用 Protobuf 了,快去试试吧!

彩蛋环节

ip 被限流了,么得 ChatGPT 玩了,自问自答两个问题吧。

Q1. 明明是 Kotlin 的 sample,为什么不直接编译成 kt 文件呢?

A1:现在的确支撑 –kotlin_out 了,仅仅因为工作中运用的环境是用 Java 写了 JNI,然后 Protobuf 生成的代码也在一同,用 Java 比较一致。别的 Kotlin 相关文档也不是很完善,不敢轻率提出替换。结构一个 Message 目标用 Kotlin 肯定会容易许多,持续重视。

Q2. 仿制了你这代码运转不起来啊?

A2:的确,文中也说到了工程中运用 Protobuf 需求额定的依靠,依靠就存在版别问题,还有跟 protoc 的兼容问题,都写出来似乎有点主次不分了。实际工作中 Protobuf 基本上是两个不同端的同事一同运用,我没想到有什么特别简略直接的单个工程能作为示例的,有想法能够教教我(真挚)。