最近有个东西, 需要将大量数据通过公网(物理上)传递到另外一台服务器, 之前采用JSON格式. 月均流量1T, 考虑到目前VPS大多数都是双向计费, 所以实际上成本居高不下.
JSON&MsgPack
玩赛马娘的时候了解到赛马娘服务器和客户端传递的是MsgPack, 首先先介绍MsgPack.
import json # 导入json库
import msgpack # 导入msgpack包, 可以通过 pip install msgpack 安装
data = {"message": "测试消息!", "code": 0, "error": None}
data_json = json.dumps(data)
data_msgpack = msgpack.dumps(data)
print(data_json)
# {"message": "\u6d4b\u8bd5\u6d88\u606f!", "code": 0, "error": null}
print(data_msgpack)
# msgpack
# b'\x83\xa7message\xad\xe6\xb5\x8b\xe8\xaf\x95\xe6\xb6\x88\xe6\x81\xaf!\xa4code\x00\xa5error\xc0'
# 比较大小
print(len(data_json.encode('utf-8')), len(data_msgpack))
# 输出: 66 36
在上面的例子中, MsgPack的体积比json的体积更小, 但是作为代价, MsgPack的可读性和JSON完全无法比较.
MsgPack包和JSON包在Python的用法几乎一致, 兼容性也很好, 在上面的用例中也将体积缩小了许多.
在性能上MsgPack据说也优于JSON, 此处不做深究.
Protocol Buffers
Protocol Buffers和MsgPack, Json都不同, 因为它需要预先定义.
在Python中, 你首先需要编写 .proto 文件, 然后使用谷歌提供的程序根据该文件生成可以被调用的python类.
Protocol Buffers同样支持许多语言(点我查看具体), 在本文只讨论Python+Protocol Buffers.
准备工作:
- protobuf包, 你可以使用 pip install protobuf 安装 [切记!]
- 用于生成类的protoc.exe, 你可以在他们在github上的项目中找到, (传送门)
首先编写好.proto文件:
syntax = "proto3";
message Response {
string message = 1;
int32 code = 2;
optional string error = 3;
}
编写好以后, 用命令行执行刚刚下载好的protoc.exe(Linux/mac用户自行带入)
.\protoc.exe --python_out=<输出路径, 当前路径就写.> .\<.proto文件路径>
注2: 谷歌文档给的不靠谱, 建议直接用这个
随后会在输出目录生成一个py文件, 在我们的测试用例中导入它:
import test_pb2
# {"message": "测试消息!", "code": 0, "error": None}
response = test_pb2.Response()
response.message = "测试消息!"
response.code = 0
data_protobuf = response.SerializeToString() # 将数据转为二进制
print(data_protobuf) # b'\n\r\xe6\xb5\x8b\xe8\xaf\x95\xe6\xb6\x88\xe6\x81\xaf!'
print(len(data_protobuf)) # 15
response_read = test_pb2.Response()
response_read.ParseFromString(data_protobuf)
print(response_read)
# message: "测试消息!"
注意: Pycharm很可能会报错, 告诉你类不存在云云, 忽略即可.
可以看到Protobuf协议将空的error和为0的code忽略, 并将数据进一步压缩, 最终数据只有15的长度, 比MsgPack还要小一般有余.
ProtoBuf协议的效率比MsgPack更高, 不过其文档的坑点相当多, 下次单独开一个坑讲解一下官方文档完全没有提及的事情.
优劣分析
很显然Protocol Buffer的数据长度更短.
就语言本身的支持度而言, 毫无疑问JSON基本上啥语言都支持, MsgPack稍弱, Protocol Buffer则更少.
JSON/MsgPack在Python中都不需要预先定义任何协议格式, 可以很方便的从字典中直接转化. 但是Protocol Buffer需要, 所以灵活性相对弱一点.
JSON/MsgPack的类型绑定相当弱, 在一些强类型要求的语言中可能会出事(如: 超过int32边界值的数字), 需要预先考虑, Protocol Buffer在定义格式的过程中便预置了格式, 所以在开发过程中能够留意到这种问题:
# {"message": "测试消息!", "code": 2147483649, "error": None}
response.code = 2147483649
# ValueError: Value out of range: 2147483649
# python的 int 基本上可以视为无边界(相对于int64等)
Protocol Buffer对pycharm这类IDE支持较差, 自动补全肯定不用想, pycharm甚至会报错(打开生成的py文件, 你确实也看不到几个类, 疑似动态生成).
Protocol Buffer的文档可谓是相当陈旧, 在学习的时候, 谷歌官方的文档给出的, 生成python类的指令是存在问题的, 文档中也没有提及python需要具体安装的包. 在bing/百度上搜索到的中文文档多为对谷歌文档的直接翻译, 而文档似乎已经过时了, 所以需要自己多琢磨.