写点什么

浅谈 protobuf

作者:lecury
  • 2021 年 12 月 21 日
  • 本文字数:1835 字

    阅读完需:约 6 分钟

浅谈protobuf

本文首次发布于知乎专栏:https://www.zhihu.com/column/c_1068890727731240960

1. protobuf 是什么?

1.1 protobuf 概述

protobuf 是 Google 公司提出的一种轻便高效的结构化数据存储格式,常用于结构化数据的序列化,具有语言无关、平台无关、可扩展性特性,常用于通讯协议、服务端数据交换场景,github 地址:https://github.com/protocolbuffers/protobuf

protobuf 的核心内容包括:

  • 定义消息:消息的结构体,以 message 标识。

  • 定义接口:接口路径和参数,以 service 标识。

通过 protobuf 提供的机制,服务端与服务端之间只需要关注接口方法名(service)和参数(message)即可通信,而不需关注繁琐的链路协议和字段解析,极大降低了服务端的设计开发成本。

1.2 protobuf 使用案例

假设有个服务端程序 SSR,期望对外提供一个注册接口,调用方传入手机号姓名邮箱三个信息,其中邮箱是选填的,那通过 protobuf 如何来实现接口定义呢?

  • step1: 定义服务名 SSR 和方法名 register

service SSR {    rpc register(RegisterRequest) returns (RegisterResponse);};
复制代码
  • step2: 定义接口参数和约束

message RegisterRequest {    required string name = 1; // 姓名    required string tel = 2; // 手机号    optional string email = 3; // 邮箱:可选};message RegisterResponse {    required int code = 1; // 错误码    optional string err_msg = 2; // 错误信息};
复制代码
  • step3: 使用 protoc 生成语言相关的协议代码

// for golangprotoc -I . -I /usr/local/include -I $(GOPATH)/src --go_out=. ssr.proto
// for cppprotoc -I . -I /usr/local/include --cpp_out=. ssr.proto
复制代码

利用官方提供的 protoc 生成语言无关的协议代码,比如 golang、C++、python 等等

  • step4:实现 protobuf 定义的接口

class SsrImpl : public proto::SSR {    void register(::google::protobuf::RpcController* controller,                    const proto::RegisterRequest* request,                    proto::RegisterResponse* response,                    ::google::protobuf::Closure* done);}
复制代码
  • step5:通过 rpc 框架注册 Service & 实现业务逻辑

// 比如baidu-rpc框架为例auto ssr = new baidu::rpc::Server();ssr->AddService(new SsrImpl(), baidu::rpc::SERVER_DOESNT_OWN_SERVICE);
复制代码

通过 protobuf 我们可以低成本的定义调用接口,并且无需关注繁琐的序列化过程,大大降低了服务端的开发设计成本。

1.3 protobuf 延伸知识

为了支持复杂数据结构和实现对数据的种种约束,protobuf 内置了自己的一套规则和语法,详细可以参考:https://developers.google.com/protocol-buffers/docs/overview

在一些特殊场景需求时,可以查阅 protobuf 开发者文档。

2. protobuf vs JSON vs flatbuf

数据交换除了使用 protobuf 外,还可以使用 JSON、flatbuf 等,这里简单介绍一下各自的优缺点和适用场景。

protobuf

  • 优势:类型安全易用性好序列化/反序列性能好兼容性好不仅可以定义结构体,还可以定义 rpc 服务接口

  • 劣势:可读性较差:没有 schema 的情况下,难以阅读和编辑。灵活性较差:无法动态修改 schema。

jsonhttps://www.json.org/json-zh.html

  • 优势:可读性好:方便理解和编辑易用性好:使用简单灵活性好:支持动态修改 schema

  • 劣势:序列化/反序列化性能差编码问题导致解析失败之类的

flatbufhttps://google.github.io/flatbuffers/

  • 优势:灵活性好性能好:序列化/反序列化几乎无耗时、高效的内存使用和读取速度。占用内存小

  • 劣势:可读性差易用性差:使用成本高,接口较为复杂

序列化/反序列化性能对比图:(来源于 https://codeburst.io/json-vs-protocol-buffers-vs-flatbuffers-a4247f8bda6f


各自的应用场景,可以参看图:

简单总结:

  1. rpc 场景定义服务接口使用 protobuf。(开发者友好 & 更好的性能)

  2. 高频序列化/反序列化场景 & 期望灵活 schema 则使用 flatbuf。(灵活的 schema & 更好的性能)

  3. 关注可读性 &开发成本则使用 JSON。(开发者友好 & 灵活的 schema)

3. protobuf 坑和小技巧

  1. 字符串如果包含非英文字符,建议使用 bytes 字段:https://developers.google.com/protocol-buffers/docs/proto#scalar

  2. 已有字段的 field number 不要修改:https://developers.google.com/protocol-buffers/docs/proto#assigning_field_numbers

  3. 慎用 required 字段,容易造成不兼容的隐患(既不能删,又不能加):https://stackoverflow.com/questions/31801257/why-required-and-optional-is-removed-in-protocol-buffers-3

  4. 路过的老哥,帮点个赞。。

发布于: 1 小时前阅读数: 3
用户头像

lecury

关注

还未添加个人签名 2018.04.27 加入

还未添加个人简介

评论

发布
暂无评论
浅谈protobuf