V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yueyoum
V2EX  ›  程序员

Json VS Protobuf

  •  
  •   yueyoum ·
    yueyoum · 2015-04-27 02:10:57 +08:00 · 13254 次点击
    这是一个创建于 3500 天前的主题,其中的信息可能已经有所发展或是发生改变。
    https://github.com/yueyoum/playground/tree/master/Json%20VS%20Protobuf

    周末两天都在搞这个东西,
    手里几个项目都用的 protobuf,最初是觉得方便,
    最近觉得 json其实比 protobuf还方便,特别是在服务端。

    于是自己就对 json 和 protobuf 在打包大小,速度方面做了测试。
    特别加入了C#,因为客户端用的这个。

    从客户端来看, protobuf 还是占有很大优势的。
    43 条回复    2015-04-28 13:39:35 +08:00
    327beckham
        1
    327beckham  
       2015-04-27 02:31:18 +08:00
    果断protobuf,在后期的扩展性来说,比json好。
    dcoder
        2
    dcoder  
       2015-04-27 03:26:43 +08:00
    觉得直接 JSON 方便很多, 特别是如果你 server 端的基本都是用 JSON 交流和存储数据的话

    还有, 你 C# 用了 newtonsoft 的 JSON 了吗? 比官方的 JSON 效率高, 很多人用这个的
    http://www.newtonsoft.com/json
    dcoder
        3
    dcoder  
       2015-04-27 03:42:22 +08:00   ❤️ 1
    翻了下lz的github项目, 猜测你是在用 C# for Mono&Unity3d
    你使用的是 LitJson 作为Json库

    我觉得最好的 JSON库 for Mono&Unity3d 是这个:
    https://www.assetstore.unity3d.com/en/#!/content/11347

    虽然有一次性的 $25 收费, 不过买到的是带源码的,
    这个plugin作者忠实地移植了.NET community评价最好的 newtonsoft 的 JSON 库
    znoodl
        4
    znoodl  
       2015-04-27 08:04:30 +08:00 via iPad
    可读上json强,楼主再比下msgpack
    patrickstar
        5
    patrickstar  
       2015-04-27 08:35:25 +08:00
    我现在需要追求性能,毫不犹豫得选择了protobuf,正在实施
    EPr2hh6LADQWqRVH
        6
    EPr2hh6LADQWqRVH  
       2015-04-27 08:54:27 +08:00
    传输protobuf, 存储bson, 公开API json
    hewigovens
        7
    hewigovens  
       2015-04-27 09:21:29 +08:00
    clino
        8
    clino  
       2015-04-27 09:23:34 +08:00
    @znoodl 感觉msgpack可读性也不好哈,不过感觉比 protobuf 简单
    garfeildma
        9
    garfeildma  
       2015-04-27 09:40:27 +08:00
    clino
        10
    clino  
       2015-04-27 10:12:53 +08:00
    楼主加个msgpack的到测试里看看?

    另外看结果比较奇怪的是,为什么python版的protobuf比json反而会慢这么多?
    amycs
        11
    amycs  
       2015-04-27 10:14:04 +08:00
    @avastms 传输也用 bson 的话有什么问题么?
    EPr2hh6LADQWqRVH
        12
    EPr2hh6LADQWqRVH  
       2015-04-27 10:28:40 +08:00
    @amycs 因为要传key值所以效率不如protobuf
    yueyoum
        13
    yueyoum  
    OP
       2015-04-27 10:29:12 +08:00
    @dcoder

    感谢 推荐, JSON.NET 已经加入我的 unity wish list了, 不过还没购买
    yueyoum
        14
    yueyoum  
    OP
       2015-04-27 10:31:52 +08:00
    @znoodl
    @clino

    msgpack 以前也试过, 感觉不到它的优势

    比json / bson 并没有多大优势。 反而又多了一个 私有协议。
    yueyoum
        15
    yueyoum  
    OP
       2015-04-27 10:33:05 +08:00
    @hewigovens

    flatbuffers 也关注过, 但是目前 支持的语言太少, 比如就没有 erlang binding

    我暂时也没时间去自己写一个。


    还是用用其他现成,成熟的吧
    yueyoum
        16
    yueyoum  
    OP
       2015-04-27 10:35:17 +08:00
    @clino

    至于 python 的 protobuf 比 json 慢,
    是因为 以前看别人也说过 官方的 protobuf 库 效率很低, 但没说有多低。

    我也是一测,才发现居然这么慢。。。
    yueyoum
        17
    yueyoum  
    OP
       2015-04-27 10:36:34 +08:00
    @avastms

    > 因为要传key值所以效率不如protobuf

    你是指 bson 吗?


    没明白这句话的意思 ,求详解
    clino
        18
    clino  
       2015-04-27 11:36:45 +08:00
    @yueyoum msgpack的效率不够好?
    应该也不会多一个私有协议,如果用应该也是替换掉protobuf吧,msgpack和protobuf应该都算私有的?
    yueyoum
        19
    yueyoum  
    OP
       2015-04-27 11:54:03 +08:00
    @clino

    恩, 我的意思是
    1. msgpack 和 json 一样, 不用先定义 协议描述, 直接 序列化/反序列化就行.

    那么此时 json是内置的, msgpack 还要安装第三方库。 这时候就没有json 方便了。

    2. protobuf 和 msgpack 都要安装第三方库. protobuf 的定义文件,就是协议文档。
    在多人合作开发的时候 有优势。 json 和 msgpack 还要自己再写一边文档。
    est
        20
    est  
       2015-04-27 12:07:37 +08:00
    效率高,还可以考虑用一个非主流的格式叫做 tnetstring

    https://pypi.python.org/pypi/tnetstring
    yueyoum
        21
    yueyoum  
    OP
       2015-04-27 12:46:01 +08:00
    @est

    感谢推荐 ,不过非主流格式 是从来就不会考虑的。
    毕竟 是要在不同语言之间 交换数据的。 所以还是得用 通用点的
    alpha1130
        22
    alpha1130  
       2015-04-27 13:15:29 +08:00
    json在后期大规模使用的时候,如果文档不完善或者相关接口资料更新不及时,绝对是个灾难
    HeyMan
        23
    HeyMan  
       2015-04-27 13:17:06 +08:00
    感觉 http 接口还是用 json 好一点,可读性强,也方便扩展,pb两端都要重新编译。内部模块传输的话 pb 体积小,性能好。
    dcoder
        24
    dcoder  
       2015-04-27 13:23:09 +08:00
    @alpha1130 你说的这个问题, 如果规划不档, 或者 msg 校验不严格, 任何 msg 格式都有这问题

    推荐 lz 看看 JSON schema, 可以做跨语言的 JSON msg 严格校验
    http://json-schema.org/examples.html
    http://json-schema.org/implementations.html
    VYSE
        25
    VYSE  
       2015-04-27 13:55:45 +08:00
    @yueyoum msgpack优势在size和speed,我们序列化数据存储和计算模块优先选择了它
    yueyoum
        26
    yueyoum  
    OP
       2015-04-27 13:57:38 +08:00
    @dcoder

    我倒不知道有个 json-schema 来规定这种事情

    不过我以前自己想过类似的实现:

    {
    "version": xxx,
    "fields": ["a", "b"],
    "a": xxx,
    "b": xxx
    }


    带有 version, 这样在一定程度上做到 不同版本兼容
    fiels, 就是来说明这个 json 有那些 key 是带着数据的


    就如同你说的, 规划好了, 其他方式也能做到这样
    neoblackcap
        27
    neoblackcap  
       2015-04-27 18:04:04 +08:00
    Apache Thrift呢?
    yueyoum
        28
    yueyoum  
    OP
       2015-04-27 18:40:57 +08:00
    @neoblackcap

    以前看过,觉得有点复杂, 就没再研究了
    alpha1130
        29
    alpha1130  
       2015-04-27 18:50:29 +08:00
    alpha1130
        30
    alpha1130  
       2015-04-27 18:52:05 +08:00
    @dcoder
    json存在这个问题,不过像protobuf, thrift不会有那么大的问题
    XadillaX
        31
    XadillaX  
       2015-04-27 18:54:47 +08:00
    我说 NBT 你会不会打我?

    https://github.com/XadillaX/mcnbt

    只是开个玩笑 2333333。
    reorx
        32
    reorx  
       2015-04-27 22:02:52 +08:00
    @alpha1130 赞同,Protobuf 相比 JSON 的优势在于它本身带有严格的 schema validation,一份 .proto 文件既作为 input/output 的入口,又作为规定数据格式的文档,在跨语言多人协作的项目上用起来不能更爽。实际上我觉得 Protobuf 和 JSON 是完全不同的两种东西,Protobuf 是 schema + data format,schema 是我们可以看见的 proto 文件里的定义,data format 是它序列化成二进制数据的格式,JSON 只有 data format…至于 JSON schema,个人觉得是一种很勉强的实现,在本身没有 schema 的数据格式上做 schema 的事情,无论是写起来还是用起来都很麻烦,说到底,JSON 只是一种轻量的、面向 Web 的 data format 罢了。
    hiboshi
        33
    hiboshi  
       2015-04-27 22:49:09 +08:00
    protobuf在Google大规模的使用 是最好的例子
    dcoder
        34
    dcoder  
       2015-04-28 02:15:06 +08:00
    @alpha1130
    @reorx
    很好的讨论, 看完对 Protobuf 更感兴趣了

    不过我再提个问题, 如果后端 service 多用 JSON 存储, 比如各种 NoSQL database 都流行存 JSON. 不只是 MongoDB, 还有很多重量级的产品都是直接用 JSON 来描述数据的, 严格与否只是 JSON format 制定者的严谨程度决定的. 比如 DynamoDB, ElasticSearch.

    这样的话, 用 Protobuf 的数据还是得再做一次到 JSON 的转化.
    我设计时候, 喜欢 Layers 尽量少, 一堆 Layer 和 data format 之间转换, 我觉得设计比较笨重.
    比如我存 JSON-based log 到 MongoDB/DynamoDB/ElasticSearch 的话, 直接用 JSON 是非常方便和简洁的设计.
    dcoder
        35
    dcoder  
       2015-04-28 02:33:07 +08:00
    @yueyoum
    还有, 一般的 JSON on Mono 库在 iOS 上是有 AOT 坑的, 所以我才专门推荐了一个 JSON .NET 库.
    同理, 你可以 Google 一下 protobuf-net Unity/Mono AOT 这些关键词组合, 应该也是有坑的.
    yueyoum
        36
    yueyoum  
    OP
       2015-04-28 10:19:57 +08:00
    @dcoder
    protobuf-net 在IOS确实有 AOT的问题, 我在 github中的说明里 特别提过了,以及解决办法。

    估计是 那篇说明太长, 大部分都没仔细看。

    就像上面还有些同学总说 web 什么的, 显然我不是做web的啊。


    关于 Layers, 我的感觉是 Layers 肯定是不会少的,

    毕竟你不会直接把数据库中的数据返回给客户端,也不会直接把客户端发来的数据存入数据库。
    肯定要加

    一层数据校验 用于接收客户端消息
    一层数据打包 用于给客户端发送消息
    一层事件发送 (可选),用于hook,或者统计

    你的意思应该是 这么多层 你都想用同样的方式, 比如json, 而不想各种格式都有 对吧?



    还有 其实说的那些 JSON SCHEMA 格式校验,
    我觉得根本没必要。

    无论是客户端还是服务器, 对于接收到的消息, 按照 预设的方式去解析, 如果解析出错
    直接报 消息格式 错误就行。
    dcoder
        37
    dcoder  
       2015-04-28 12:01:41 +08:00
    @yueyoum
    是的, 只是在 JSON 这一个通用格式上做各种逻辑, 这种统一格式的设计会非常简洁
    JSON SCHEMA 是说基本的 filed 校验逻辑不用再写了, 因为在不同语言中再写一遍还是挺麻烦

    另外, 抱歉我没看到你处理 protobuf-net AOT 的方法, 能麻烦你贴一下链接么, 学习一下 :)
    reorx
        38
    reorx  
       2015-04-28 12:09:44 +08:00
    @dcoder 其实 JSON 和 Protobuf 我都很喜欢,两者都有自己的优点,根据场景恰当地挑选要使用的技术就好了。

    我觉得 JSON 使用的场景一般是,不太需要考虑内部协定的 schema,或者说多个服务之间使用的数据格式并不强要求是一致的。比如一个 Web 后端 A,内部依赖服务 B,A 暴露给客户端的接口是 RESTful 的 JSON API,A 调用 B 时通过 B 提供的 RPC library,整个服务场景中存在两种接口和数据格式,互相没有什么关联,也不要求一致,这时用 JSON 就很好(原本 JSON 就是所有数据格式中最 human readable 的),每个服务自己维护自己的解析和参数验证。

    Protobuf 一般是涉及到多个服务之间的数据交换,且每个服务内部用的数据要求用一致的 schema,这种大部分都不是 Web 项目,一般都是用网络编程框架做的传统后端,非常依赖 RPC 调用的服务。这时用 Protobuf 或者 Thrift 就非常好了,因为其实 Protobuf、Thrift 都是提供了一种序列化结构数据的方法,然后提供了一种用于描述结构数据的语法规则,在这个基础上其实已经把 data format 给抽象不见了,我们不需要知道它底下序列化后的二进制数据长什么样子,我们只关心每个使用同一份 proto 定义的服务内部的数据都是对应一致的 schema,且可以无缝转移到另一个服务中使用(这个过程我们也不需要关心)。如果我选择 Protobuf 而非 JSON,必然是因为它在打通跨服务的数据一致性上的优点,而不是它相比 JSON 性能有何优势。

    当然最终选择的时候还有个很关键的因素,就是整个大环境的技术选型,如果你所依赖的其他组件用 JSON 的较多,那么也不要刻意用 Protobuf 搞得大家都很麻烦。如果一个大型项目在一开始就用了 Protobuf 作为数据格式的约定,后期在数据一致性上可能出现很多麻烦就可以避免掉呢。

    最后说点我使用 Protobuf 的经验,项目里的每个 proto 文件都会在最上面标注一个 version number,所有的 required 字段一经定下允许再更改,对 proto 文件的提交必须保证严格的 review,不能随便 merge 到 master,如果被多个项目需要甚至会把 proto 文件单独放到一个 git repo 里,在其他需要它的地方用 git submodule 引用。

    P.S. 写完发现用了好多个「一致」,有点啰嗦,赶吃饭就先这样了…
    reorx
        39
    reorx  
       2015-04-28 12:11:04 +08:00
    「所有的 required 字段一经定下允许再更改」→「所有的 required 字段一经定下 **不允许** 再更改」orz
    yueyoum
        40
    yueyoum  
    OP
       2015-04-28 12:51:28 +08:00
    @dcoder

    用 precompile 来应对 IOS 的 AOT 问题
    我是参考的这个帖子: http://game.ceeger.com/forum/read.php?tid=13479

    我直接写一个 命令行工具来做这些事情: https://github.com/yueyoum/pg
    yueyoum
        41
    yueyoum  
    OP
       2015-04-28 12:57:38 +08:00
    @reorx

    你说的对, JSON 和 protobuf 肯定是要用在各自合适的环境中

    比如我的项目:

    服务器 , 客户端之间是 protobuf
    服务器各个组件之间的API调用 是 json

    我之所以有那个对比的测试, 是想测试一下 在 服务端和客户端用 gzip 压缩的json,看到底是否可行。
    从结果来看,erlang/python服务端基本没影响, c#客户端性能降低了好多。
    结论就是 如果是 弱交互,消息通信不频繁的项目, 直接上json就行。不过最好gzip压缩一下


    至于你后来说的, **一旦定义过的required字段,不允许更改** 这个我不赞同

    有些功能一改再改,到最后直接换个方式玩法, 消息大概是必然的。

    所以这才是版本控制的重要性。
    reorx
        42
    reorx  
       2015-04-28 13:17:17 +08:00
    @yueyoum 嗯嗯,是的,其实不可能有那么严格的限制,毕竟业务需求是在变化的,我那样说主要还是强调 schema 不要轻易变化,倒不是说用 Protobuf 就一定要那样。不过我自己在团队内部确实是这样要求的,required 在设计确定下来之后基本上就不会改了,新的需求通过增加 optional 来解决,有这样的约束大家会非常谨慎,反复推敲,多方确认,来保证所设计的 schema 是可以兼顾可见的未来的需求的。

    所以「不允许更改」==「设计应该足够完善以至理论上不应该产生更改」,警戒意义多于实际约束 XD
    dcoder
        43
    dcoder  
       2015-04-28 13:39:35 +08:00
    @yueyoum marked
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1158 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 23:39 · PVG 07:39 · LAX 15:39 · JFK 18:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.