我们知道接口传输数据的格式有XML,它是 web services 使用的传输数据的格式,在web services 中叫 WSDL。

https://www.w3school.com.cn/wsdl/index.asp

不过 web Services 已经是被淘汰的技术了。

现在更是主流的是使用JSON作为数据传输格式。HTTP + JSON是黄金搭档。

什么是protocol buffer

protocol buffer是一种与语言和平台无关。

Protocol Buffer是用于结构化数据串行化的灵活、高效、自动的方法,有如XML,不过它更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。

protocol buffer 是如何使用

  • 定义 xx.proto文件,即 接口数据格式。
  • 使用 proto 命令生成对应语言脚本。
  • 通过语言脚本设置接口要发送/接收的数据。

定义 .proto 文件,

创建一个foo.proto 文件中定义你需要做串行化的数据结构信息。每个ProtocolBuffer信息是一小段逻辑记录,包含一系列的键值对。

这里定义一个简单的 foo.proto 文件定义了个人信息:

syntax = "proto2";
package info;

message Person {
    required string name=1;
    required int32 id=2;
    optional string email=3;

    enum PhoneType {
        MOBILE=0;
        HOME=1;
        WORK=2;
    }

    message PhoneNumber {
        required string number=1;
        optional PhoneType type=2 [default=HOME];
    }

    repeated PhoneNumber phone=4;
}

这个信息就是你的接口要发送的数据格式。

文件说明:

syntax = "proto2";

文件最开始建议说明使用proto2还是proto3语法的声明。

package info;

文件必须以 package xxxx; 声明开头,作为协议唯一的标识,避免不同项目的命名冲突,你可以理解成我给一个人起名叫张三,如果要想找这个人,就要叫一声张三。

message Person {
    ...
    message PhoneNumber {
        ...
    }
}

一个 message 相当于一个指定类型的集合,例如 bool, int32, float, double ,string 这些类型都是可以直接使用在proto协议中的某个message当中指定数据类型的。

一个message可以直接嵌套另一个message使用,被嵌套的message就相当于string一样,被认为是一种数据类型。

required string name=1;
required int32 id=2;
optional string email=3;
...
repeated PhoneNumber phone=4;

每个message的字段必须要声明是requiredrepeatedoptional

Required: 表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。

Optional:表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。

Repeated:表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值。

好了,你现在对 .proto 文件的定义有了大概的一个了解了。

使用 proto 命令生成对应语言脚本

1、下载 protobuf 文件

https://github.com/protocolbuffers/protobuf/releases

以windows为例,下载下面两个文件:

  • protobuf-python-3.12.3.zip
  • protoc-3.12.3-win64.zip

2、 先解压 protobuf-python-3.12.3.zip 文件,得到 protobuf-3.12.3目录,再解压 protoc-3.12.3-win64.zip 文件得到 protoc 目录,把你 protoc 目录放到 protobuf-3.12.3目录。

3、 设置环境变量path

D:\pybase\protobuf-3.12.3\protoc\bin

将上面的路径添加环境变量。

4、 执行 proto 命令,生成python脚本。利用前面创建 foo.proto 文件。

protoc --python_out=. foo.proto

你会看到同目录下多出一个 foo_pb2.py 的文件。不用打开看了,反正你也看不懂。

通过语言脚本设置接口要发送/接收的数据

现在我们利用 foo_pb2.py 文件定义发送的接口数据。 创建一个test_foo_pb2.py文件。

import foo_pb2


def set_info(info_):
    info_.id = 1
    info_.name = "tom"
    info_.email = "tom@gmail.com"

    phone = info_.PhoneNumber
    phone.phone = 400100
    phone.type = 2

    return info_


info = foo_pb2.Person()

one_info = set_info(info)
print("设置数据:\n", one_info)

proto_info = one_info.SerializeToString()
print("序列化:\n", proto_info)


def get_info(wanted_info):
    """
    反序列化的数据
    """
    print("反序列化:\n")
    wanted_id = wanted_info.id
    print("info id:", wanted_id)
    print("his age: ", wanted_info.name)

    print("his phone number :", wanted_info.PhoneNumber.phone)
    print("his phone type :", wanted_info.PhoneNumber.type)


first_parsed = foo_pb2.Person()
first_parsed.ParseFromString(proto_info)
get_info(first_parsed)

打印结果:

设置数据:
 name: "tom"
id: 1
email: "tom@gmail.com"

序列化:
 b'\n\x03tom\x10\x01\x1a\rtom@gmail.com'
 
反序列化:
info id: 1
his age:  tom
his phone number : 400100
his phone type : 2

我帮你问:这有什么用?

想想当你在发送数据的时候,以 序列化的数据发送,是不是更节省资源?是不是更安全?