您当前的位置:首页 > 互联网教程

数据序列化工具 Protobuf 编码&避坑指南

发布时间:2025-05-19 22:24:13    发布人:远客网络

数据序列化工具 Protobuf 编码&避坑指南

一、数据序列化工具 Protobuf 编码&避坑指南

1、数据序列化工具 Protobuf的编码与避免常见问题指南

2、在现代通信和数据交换中,Protocol Buffers(protobuf)是一种广泛应用的协议,它由Google开发,以高效、紧凑的格式承载协议、配置和数据库信息。然而,使用它也需注意一些关键点,以确保正确理解和使用。下面我们将深入了解其编码原理以及使用中的注意事项。

3、首先,protobuf编码紧凑,以message结构为基础。例如,每个message包含一系列tag(包含字段编号和类型信息)和对应的值。没有.proto文件,数据是无法解析的。它的编码规则要求字段编号不超过15时,用一个byte表示;超过15的字段编号需两个byte。

4、在编码时,protobuf对数字类型进行了优化,如使用Varints编码,127以内的整数仅占用2byte。对于负数,ZigZag编码能节省空间,尤其是对于大量负数的数据,使用sint类型会更高效。

5、嵌套消息和重复字段的处理也需要注意。嵌套消息会计算整体大小,而重复字段则以列表形式存储,每个值单独编码。此外,最佳实践是保留1-15的字段编号,因为它们占用字节数较少。

6、在更新协议时,保留未使用的字段并标记为reserved可以避免客户端和服务端的不一致,同时避免随意修改字段tag和类型,这可能导致解码错误。protobuf 3已移除required关键字,以保持兼容性。

7、在处理大数时,尽量选择小整数,以利用Varints的压缩优势。对于负数,sint32或sint64可以节省空间。最后,了解并参考相关文档如sunyunqiang.com、halfrost.com和protobuf的官方文档,能帮助你更深入地掌握和避免protobuf的常见问题。

8、了解这些要点,你将能更有效地使用Protobuf,确保数据序列化的高效和一致性。

二、为什么Protobuf的默认序列化格式没有包含消息的长度与类型

1、Protobuf是经过深思熟虑的消息打包方案,它的默认序列化格式没有包含消息的长度与类型,自然有其道理。哪些情况下不需要在 protobuf序列化得到的字节流中包含消息的长度和(或)类型?我能想到的答案有:

2、如果把消息写入文件,一个文件存一个消息,那么序列化结果中不需要包含长度和类型,因为从文件名和文件长度中可以得知消息的类型与长度。

3、如果把消息写入文件,一个文件存多个消息,那么序列化结果中不需要包含类型,因为文件名就代表了消息的类型。

4、如果把消息存入数据库(或者 NoSQL),以 VARBINARY字段保存,那么序列化结果中不需要包含长度和类型,因为从字段名和字段长度中可以得知消息的类型与长度。

5、如果把消息以 UDP方式发生给对方,而且对方一个 UDP port只接收一种消息类型,那么序列化结果中不需要包含长度和类型,因为从 port和 UDP packet长度中可以得知消息的类型与长度。

6、如果把消息以 TCP短连接方式发给对方,而且对方一个 TCP port只接收一种消息类型,那么序列化结果中不需要包含长度和类型,因为从 port和 TCP字节流长度中可以得知消息的类型与长度。

7、如果把消息以 TCP长连接方式发给对方,但是对方一个 TCP port只接收一种消息类型,那么序列化结果中不需要包含类型,因为 port代表了消息的类型。

8、如果采用 RPC方式通信,那么只需要告诉对方 method name,对方自然能推断出 Request和 Response的消息类型,这些可以由 protoc生成的 RPC stubs自动搞定。

9、对于最后一点,比方说 sudoku.proto定义为:

10、rpc Solve(SudokuRequest) returns(SudokuResponse);

11、那么 RPC method Sudoku.Solve对应的请求和响应分别是 SudokuRequest和 SudokuResponse。在发送 RPC请求的时候,不需要包含 SudokuRequest的类型,只需要发送 method name Sudoku.Solve,对方自知道应该按照 SudokuRequest来解析(parse)请求。这个例子来自我的半成品项目 evproto。

12、对于上述这些情况,如果 protobuf无条件地把长度和类型放到序列化的字节串中,只会浪费网络带宽和存储。可见 protobuf默认不发送长度和类型是正确的决定。Protobuf为消息格式的设计树立了典范,哪些该自己搞定,哪些留给外部系统去解决,这些都考虑得很清楚。

13、只有在使用 TCP长连接,且在一个连接上传递不止一种消息的情况下(比方同时发 Heartbeat和 Request/Response),才需要我前文提到的那种打包方案。(为什么要在一个连接上同时发 Heartbeat和业务消息?请见陈硕《分布式系统的工程化开发方法》 p.51心跳协议的设计。)这时候我们需要一个分发器 dispatcher,把不同类型的消息分给各个消息处理函数,这正是本文的主题之一。

三、基于Protobuf 序列化的异步 RPC -- TinyRPC 框架

1、在本文中,我们将探讨基于Protobuf的异步RPC服务的实现,利用TinyRPC框架。在实际互联网应用中,Protobuf常用于RPC通信,而非HTTP。TinyRPC是一个高性能的C++协程RPC框架,已基本完成开发,本文将详细介绍如何搭建RPC服务。

2、首先,TinyRPC框架使用自定义的TinyPB协议,一种基于protobuf的简单报文格式,设计灵感来源于《Linux多线程编程--使用muduo网络库》。最小报文长度为26字节,包含错误码(表示调用状态)、完整方法名以及序列化后的数据。

3、要实现RPC服务,你需要定义一个Protobuf文件,如tinypb.proto,并生成pb桩文件。这些文件包含了RPC主要类,如QueryService和其Stub。继承QueryService并重写query_name和query_age方法,实现业务逻辑,例如从MySQL查询用户信息。

4、配置文件中,要选择TinyPB协议,并配置数据库连接信息。服务实现则涉及注册QueryServiceImpl到RPC中,通过RpcChannel进行异步调用。异步特性体现在TinyRPC框架的协程机制中,调用非阻塞,提高性能。

5、最后,你可以通过修改HTTP服务调用RPC服务,进行简单测试。测试代码和文档可以在GitHub项目中找到,欢迎读者尝试。如对内容满意,请给予点赞和关注支持开源项目。