从去年开始我陆陆续续把几个负责的服务从JSON迁移到了二进制序列化方案起因其实很简单线上有个数据上报接口QPS上来之后CPU和带宽双双告警而全链路里最贵的一块不在业务逻辑本身而是每次请求里那段几百字节的JSON字符串反复地序列化、反序列化、再通过网络传出去。排查下来光是JSON解析就吃掉了将近四分之一的CPU时间片这让我不得不重新审视“文本序列化够用”这个默认前提。如果你也在做后端服务、游戏服务器、IoT网关或者大数据管道整天和REST接口、消息队列打交道那你大概率和我有过同样的处境JSON方便、直观、调试友好但到了高吞吐、低延迟、资源受限的场景里文本序列化的弱点会被放大到很难忽视。这篇内容就是围绕“二进制序列化优于文本序列化”这个结论展开的我会从底层原理、量化测试、方案选型、实操改造到踩坑排查一条线讲清楚不绕弯子直接给你能用的结论和代码。1. 整体设计思路与方案选型逻辑1.1 文本序列化为什么会在高并发下先“露怯”要理解二进制序列化优势在哪得先看清文本序列化到底做了什么。以JSON为例当你调用json.dumps({“name”: “test”, “age”: 18})底层干的事情是把键名变成字符串、把整型数字转成ASCII字符、把字符串里的特殊字符做转义、再加上引号、冒号、逗号、花括号这些结构符号。最终传输和存储的是一串人类可读的字节。这个设计换来了极致的调试便利性但代价也隐藏在这里数据体积膨胀严重。键名被完整重复存储结构符号占用大量字节数字转成字符串后往往比原始字节数多出50%到数倍。解析代价高昂。字符串需要逐字符扫描、语法校验、动态类型转换每一步都是CPU密集操作。内存抖动大。解析过程频繁创建临时字符串和中间对象短生命周期对象暴增直接推高GC压力。弱类型导致额外转换开销。数字从字符串变回int、float需要额外的计算路径不像二进制方案直接按类型读取。这些特性在低频交互场景下不算大问题但一旦进入高并发、大流量或者嵌入式环境就变成了实打实的瓶颈。我习惯用一个类比来说这个事文本序列化相当于把货物先拆成零散的、写着标签的零件装进纸箱搬运时还要防止标签损坏二进制序列化则是把货物直接压进一个按图定制的标准集装箱箱体尺寸、货物位置都是事先约定好的装卸都快得多。1.2 二进制序列化赢在哪些关键维度二进制序列化的核心思路是通信双方事先约定好数据结构布局序列化时直接按固定规则把字段值写入字节流反序列化时按同样的规则从字节流里按偏移量读取。这种思路带来的收益是全方位的体积显著缩小。没有重复的键名没有结构符号数字以紧凑的定长或变长格式存储。实际测试中同样的业务数据二进制方案体积通常只有JSON的30%到60%。解析速度大幅提升。不需要字符串扫描和语法分析直接按字段类型从缓冲区读取本质上是内存拷贝级别的操作。内存分配次数减少。合理设计的二进制解码器可以做到一次分配、全部填充中间对象数量比JSON解析少一个量级。类型安全。字段类型在编译期或schema定义阶段确定解码时不用做动态类型推断也避免了很多隐式转换带来的坑。对低带宽、弱设备友好。同样的信息量占用更少的网络字节对IoT设备、弱网环境是质的改善。当然二进制序列化不是免费的午餐它牺牲的是可读性和调试便利性。你没法直接用curl或者浏览器看到响应内容。但从业者的角度讲这个代价是可以通过工具链和规范来弥补的而性能收益是实实在在的。1.3 什么场景下“优于”成立什么场景下要冷静我需要把话说得严谨一点二进制序列化优于文本序列化这是一个有适用边界的前提判断。下面这些场景里这个结论几乎无条件成立微服务内部的高频RPC调用网络开销和序列化开销直接影响吞吐上限。游戏服务器、实时协作系统要求低延迟且单次消息频繁。IoT设备与网关通信带宽和内存都非常紧张。大量数据落盘存储比如日志归档、特征数据、监控指标体积直接决定存储成本。消息队列的大批量投递每条消息的payload体积影响broker的吞吐和磁盘IO。反过来下面这些场景里我建议你继续用JSON甚至YAML对外提供公共API调用方不可控调试需求占比高。配置文件的读写人工编辑频率远高于程序解析频率。请求量不大、完全够用的情况不值得为性能优化增加复杂度。跨团队协作且上下游没有统一schema管理体系时。认清场景边界然后做选型这才是工程师该有的判断逻辑。我自己在团队里定过一个原则内部链路优先考察二进制方案外部接口保持JSON兼容两层之间做协议转换。这样既能拿到性能也不牺牲生态兼容。2. 核心细节解析与实操要点2.1 一个完整的数据结构在两种方案下的体积对比空口说体积差异不够直观我拿一个典型的业务数据结构来做实际对比。假设我们要传输一个用户注册事件包含用户ID、昵称、年龄、注册时间戳和一段设备信息{ user_id: 10012345, nickname: playmaker, age: 27, register_ts: 1700000000, device: { os: android, version: 14.0 } }这段JSON字符串我数过去掉所有空白字符之后大约是112字节。现在用常见的二进制序列化方案编码相同的数据user_id用int32变长编码10012345实际占4字节昵称字符串加一个长度前缀存UTF-8字节playmaker总共10字节age是int32占1字节变长编码下27只需要1字节register_ts是int64变长编码1700000000占5字节device对象里os和version字符串各加长度前缀分别是7字节和5字节。总体算下来二进制payload大概在32到38字节之间。相比JSON的112字节压缩比大约是三比一。这还没算HTTP/JSON场景里额外的Content-Type头、字符集标识等开销。数据字段嵌套越深、键名越长二进制方案的体积优势就越明显。2.2 序列化和反序列化的性能开销拆解体积之外我更关注CPU开销。这里可以做一个很简单的基准测试思路同样的数据循环执行序列化和反序列化各一百万次比较耗时。我实际在某公司的性能测试环境里跑过一组对比环境是基于x86架构的普通服务器结果大致如下JSON序列化约520ms每百万次反序列化约780ms每百万次。反序列化明显更重因为要多做键名匹配和类型转换。二进制序列化约180ms每百万次反序列化约140ms每百万次。也就是说在序列化和反序列化的全链路耗时上二进制方案比JSON快了大约三到四倍。为什么反序列化更快因为JSON反序列化要先做字符串的语法解析再根据键名动态匹配字段最后用反射或者映射机制把值填进结构体二进制反序列化则相当于对着“字节布局图纸”直接按偏移量搬数据不需要任何字符串比对。GC压力上的差异同样值得关注。JSON解析过程中每次键名比对都产生临时字符串每个数字转换都产生中间对象。我做过一次粗略统计处理一千条JSON记录短生命周期对象数量大约是一千条二进制记录的三倍以上。对象越多GC越频繁延迟毛刺就越明显。这在追求稳定的在线服务里是很致命的。2.3 主流二进制序列化方案的横向对比二进制序列化不是一个单点技术而是一个工具家族。不同方案在性能、易用性、跨语言能力、schema演进友好度上各有取舍。我给主流方案做了个对比表方案跨语言Schema演进零拷贝支持压缩率易用性适合场景Protobuf优秀优秀一般高中微服务RPC、高吞吐后端FlatBuffers优秀良好优秀中低游戏、高频读取场景MessagePack良好无内置一般中高高需要兼容JSON的快速场景CBOR良好无内置一般中高IoT、标准有要求的场景Avro优秀优秀一般高中大数据管道、Hadoop生态语言内置方案差差一般中极高单语言内部临时使用方案选型这件事我的核心建议是有跨语言需求第一时间看Protobuf数据读取频率极高且要求低延迟的认真考察FlatBuffers只求快速改造成本低MessagePack是很好的中间路线大数据场景无脑选Avro。语言内置方案如Python的pickle、Java的原生序列化除非是写一次性脚本否则不推荐在生产链路里使用它既不跨语言又天生自带安全风险反序列化不可信数据时可能直接导致远程代码执行。2.4 为什么protobuf类的schema设计决定了上限在众多二进制方案里Protobuf值得一提因为它把“结构约定”这件事制度化、版本化了。Protobuf的消息定义本质上就是一份机器可读的结构蓝图通信双方都基于这份蓝图工作。它的varint变长整数编码、tag-value结构、字段编号代替字段名等机制是它体积小和解析快的关键。用字段编号替代字段名这一点是最精妙的设计。JSON每次都要携带完整键名“user_id”Protobuf里只是用一个数字tag比如field 1来标识字段。这个巧妙的替换让体积直接缩小还不损失扩展性。字段编号一旦确定后续如果新增字段老客户端打开新数据包时会自动跳过未知字段新客户端读取老数据包时缺失字段走默认值这就是优秀的向前向后兼容能力。但这套设计也要求团队具备schema治理能力字段编号不能随意复用、废弃字段要预留tag、发布时要保证兼容性检查。没有流程约束迟早会出现“字段编号被错误复用导致线上数据错乱”的灾难。所以使用Protobuf等带schema的方案必须配套一套发布规范和兼容性检查流程这是使用这种方案的基本盘。3. 实操过程与核心环节实现3.1 三步走从JSON到Protobuf的实际改造路径我在某跨平台系统里做过一次比较完整的改造从JSON协议迁移到Protobuf。整个流程可以归纳为三步。第一步定义schema文件。这是整个改造的起点也是决定后续成败的关键。我当时把一个包含二十多个业务接口的动态消息格式全部梳理成了proto文件。这里有个经验schema定义阶段一定要拉上上下游的负责人一起review任何字段的缺失或类型错误在序列化方案里比在JSON里代价大得多。JSON多一个字段少一个字段解析时通常只是多一个undefinedProtobuf类型不匹配时轻则解析失败重则数据静默截断或溢出。第二步生成代码并替换序列化点。这一步相对机械但要注意修改埋点时的隐蔽问题。我经历过一个坑某个上报接口在日志打印时用了老的JSON序列化器结果日志文件里全是正常可读内容但真正走网络的那段代码已经换成了Protobuf。调试的时候对着日志看半天以为线上还是JSON造成误判。建议在改造期间统一加一个协议标识头快速区分链路中的实际编码方式。第三步压测验证和灰度发布。改造完成后先跑一遍全链路压测验证CPU、内存、延迟指标的变化再按5%、20%、50%、100%的比例灰度放量。不要一次性全量切因为二进制协议在出问题的时候问题表象会和文本协议完全不同排查成本也更高。3.2 一个可运行的Protobuf示例与字节解释为了让理解落地给一个最小可运行的Protobuf示例。定义文件大致如下syntax proto3; message UserEvent { int32 user_id 1; string nickname 2; int32 age 3; int64 register_ts 4; DeviceInfo device 5; } message DeviceInfo { string os 1; string version 2; }保存为event.proto之后在项目里安装对应语言的protoc编译器执行代码生成指令。以Go环境举例protoc --go_out. event.proto生成代码后序列化和反序列化的调用就非常简单了userEvent : UserEvent{ UserId: 10012345, Nickname: playmaker, Age: 27, RegisterTs: 1700000000, Device: DeviceInfo{Os: android, Version: 14.0}, } // 序列化 payload, err : proto.Marshal(userEvent) // 反序列化 var decoded UserEvent err proto.Unmarshal(payload, decoded)这段代码生成的二进制payload第一层结构大致如下第一个字节是tag值为0x08二进制是00001000低3位的000表示wire type是varint高5位的1表示字段编号是1。后面的字节是varint编码的user_id10012345编码成四个字节。接着是字符串字段的tag以及长度前缀和UTF-8字节内容。如果你用十六进制工具去dump这段payload你会看到消息不是人类可读的但每个字节的意义都能对着schema解释清楚。这正是二进制序列化的精髓可读性让渡给了紧凑性和速度但对机器来说是最高效的表达方式。3.3 调试与观测手段没有可读文本怎么定位问题好多人对二进制序列化的排斥根源在于“看不见”。我承认第一次在黑框里看到一堆乱码似的字节时我也觉得没底。但调试二进制协议其实有一套成熟的方法论核心是“分层观测”。第一层抓包看结构。Wireshark、tcpdump这些工具都有适用于Protobuf的解析插件配置好proto文件后抓包能直接看到解码后的消息内容。第二层日志里留一个可读副本。线上环境可以将二进制payload做一次hex dump或者base64编码后打入日志必要时再写个离线脚本把十六进制字节对照schema反解成人类可读的JSON方便排查。第三层用好序列化框架自带的反射能力。很多语言运行时提供消息描述符接口可以在不依赖原始结构体的情况下把二进制消息解码成一个通用Map。我常用这个方法写一些诊断工具效果很好。调试成本是可管理的关键是要在一开始就把工具链建好而不是等到线上出问题了才临时凑。3.4 性能调优中的关键参数与buffer管理实践在代码层面几个参数和写法直接决定最终性能这里挑重点讲。序列化的核心是缓冲区管理。官方库通常提供两种方式直接Marshal或者在已知结构的情况下复用缓冲区。直接Marshal最简单但每次都会分配新的字节数组在超高频率下会造成不小的内存分配压力。更优的做法是预分配buffer把序列化结果写入复用缓冲区。Go的proto.MarshalOptions、proto.UnmarshalOptions里就可以配置复用逻辑。提前分配适当大小的slice配合sync.Pool复用可以大幅减少GC压力。我在实操中会把预估消息大小定义成常量根据业务情况动态扩容。这里有个原则扩容时按指数增长而不是每次加固定大小否则遇到体积波动大的消息会产生多次拷贝反而拖累性能。另一个容易被忽视的优化点是避免反复构造同一批消息对象。生产者线上很多代码是“每次来一个请求就new一堆结构体”这在低频场景没毛病高并发下就非常浪费。合理调整对象复用策略让同一个结构体在不同事件间重置复用能显著降低分配次数。最后关注GC参数。二进制序列化虽然降低了对象数量但对Java、Go这类带GC的语言优化时还是要看具体的GC日志和heap profile。改造后跑一轮pprof或者jstat确认暂停时间是否真正缩短这才算闭环。4. 常见问题与排查技巧实录4.1 兼容性问题新老客户端混布时的数据错乱这是改造过程中最常见的坑。线上服务不可能一夜间全量升级老客户端还在使用JSON新客户端已经切到二进制服务器要同时识别两种协议。我在实践里用的是“协议指纹识别”方案在每个消息头部加一个魔数Magic Number和版本号服务端先读前几个字节匹配二进制魔数就走新解码路径否则回落JSON解析。有的团队选择用HTTP的Content-Type或者gRPC的Content-SubType来区分这也行但要注意代理、网关层不要清洗掉这些头信息。我们当时就踩过这个坑内网网关配置了默认的Content-Type重写导致二进制请求被错误的当成JSON解析报错信息极其诡异。改协议时务必设计好过渡期的兼容策略。时间上要给足重放缓冲流量上要灰度递进。凡是没考虑兼容过渡的协议替换最后都会变成线上事故。4.2 类型溢出与字段截断为什么数字会突然不对二进制方案的强类型在绝大多数时候是优点但遇到跨语言、跨版本传输时类型不匹配会带来很隐蔽的问题。最典型的是int32和int64混用某个字段在A语言里定义为int32B语言里定义成int64如果某个值超过int32上限B写入时正常A解析时直接溢出。还有无符号和有符号的混用问题。比如把负数写入uint32字段序列化后反序列化出来会变成一个极大的正整数这类错误在没有单元测试覆盖的情况下很难被发现。我建议每个涉及跨语言传输的字段都要在schema评审时明确取值范围并且在关键字段上补充代码层的数值校验逻辑宁可多一次判断也不要让错误数据悄悄流到下流系统。4.3 缓冲区越界与半包问题网络传输层的配套改造这是另一个高频坑。二进制序列化只是把对象变成字节流但字节流在网络传输时会被拆成多个TCP分片。接收方必须自己处理“半包”和“粘包”问题。JSON通常自带边界符号解析器可以自动识别消息结束位置二进制协议没有天然的边界标记必须在消息头里携带完整的长度信息。我的做法是采用经典的“4字节长度前缀 payload”帧格式先读4个字节得到整条消息的字节长度再根据长度读取完整payload最后丢给反序列化器。这个方案简单可靠配合一个合适的read buffer大消息也能平稳处理。这里有个关键细节不要把长度字段和payload分两次网络调用拼接而是尽量做“读满一个完整帧再解析”否则处理半包时的状态维护会越写越复杂bug层出不穷。4.4 线上问题排查速查表把我在实战中积累的排查经验整理成一张速查表直接默认大家会需要现象可能原因排查方法反序列化直接报错客户端与服务端schema定义不一致对比两端proto文件重点看字段编号和类型数据能解析但值明显不对数据类型跨语言不匹配int32/int64、有符号/无符号检查两端类型定义用边界值做单元测试偶发性丢包、卡顿半包/粘包处理不完善抓包检查帧格式确认长度字段是否正确、是否缺读或多读延迟反而变高序列化前端的对象创建过多或buffer频繁扩容做内存profile看对象分配热点老客户端连不上兼容策略未生效检查协议指纹和版本号确认网关或代理未改动头部部分字段默认值丢失proto3的默认值不参与序列化接收端拿到空值确认字段语义必要改用显式包装类型跨语言解出的字符串乱码字符编码不一致统一UTF-8禁止GBK等编码参与序列化这七类问题基本覆盖了我见过的大部分线上事故每次排查都能回到schema、帧格式、类型匹配和缓冲区管理这四件事上。4.5 避坑要点重复造轮子和过度设计同样危险最后说两个方向性警示。第一个警示是别重复造轮子。有人觉得自研序列化很简单自己写一套字节编码规则性能可能还更好。我见识过几次自研方案活下来的少、留下坑的多。真实结果通常是编码规则文档不全、老员工离职后无人能维护、线上数据无法与新代码解析、工具链完全没有。除非你是钻研技术的极客且项目生命周期很短否则真的没必要。第二个警示是别过度设计。如果你的场景只有每秒几十次请求、数据量小得可怜那么从JSON切换到二进制方案对你来说省下的CPU和带宽微乎其微但增加的调试成本、工具链成本、协作成本却是实打实的。技术选型是工程决策不是赶时髦。选择最优方案的前提是先准确量化当下的真实瓶颈。5. 工具选型解析与生态配套5.1 语言生态下的最佳搭配不需要用到某种方案时乱折腾我这里直接给出语言视角下的推荐组合纯属个人的使用参考。Go生态首选Protobuf配gRPC。原生支持完善性能好工具链成熟。如果只是单机内部通信且不想引入额外依赖也可以考虑Go自带的gob但仅限于无兼容性要求的内部场景。Java生态Protobuf、Avro都很成熟。如果缓存对象要持久化到Redis我倾向于用Protobuf序列化后再存配合Kafka则Avro很有优势因为Kafka生态本身对schema registry有天然支持。C生态FlatBuffers值得优先尝试它主打零拷贝访问游戏服务器和延迟敏感系统里表现极好。在某些高并发读场景FlatBuffers比Protobuf快一个档次。Python生态注意Protobuf是首选但Python本身就是解释型语言序列化的整体速度受限于Python运行时收益不如编译型语言明显。如果瓶颈在解析上可以考虑把超高频路径用C扩展或异步协程来优化。5.2 序列化框架之外的配套组件二进制序列化只能解决“数据编码”这一环完整的性能改造还需要配套组件。这里推荐三件套连接池和连接复用避免频繁建立连接的握手开销。批量发送把多条小消息合并成一个批次发送能明显减少网络往返次数但要注意单条消息的最大体积限制。压缩策略二进制序列化之后如果数据体积仍然偏大可以再加一层压缩比如Snappy或者Zstd。很多RPC框架支持透明压缩选传输层压缩算法时优先看解压速度而非压缩率在延迟敏感系统里Snappy这类无内存分配或极低内存分配的算法更合适。这三件套配合二进制序列化才能构成一条完整的低延迟、高吞吐数据链路。单独只换序列化方案收益会打折扣。5.3 性能评估方法论不要被基准测试数据迷惑我见过不少团队被网上某些benchmark的炫目数据误导兴冲冲换了方案结果线上一测毫无改善。原因是基准测试的负载模型和线上完全对不上。做序列化性能评估我建议你关注四个指标并配合实际流量回放来验证序列化耗时P50/P99尤其关注P99因为高延迟毛刺比平均值的恶化影响更大。对象分配的字节数和分配次数决定GC压力。序列化后payload的实际体积关系到带宽和存储成本。端到端的RTT请求往返时间这是最终用户感知的指标序列化只是其中一环。任何脱离线上流量模型的基准测试都只是参考不能直接作为选型依据。正确做法是写一个小的压测工具录制线上请求样本在测试环境回放并对比指标。这套流程虽然前期繁琐但能在切换方案前就把风险暴露出来。6. 影响范围与应用场景深度分析6.1 微服务架构下的连锁收益二进制序列化在微服务架构里产生的收益是叠加的。服务间调用频繁每次RPC少传几十字节对同一个服务来说可能微不足道但对经过4到5跳的调用链端到端节省的字节数会成倍累加。在一天的亿万级调用量下省下的总带宽是一个很可观的数字。更重要的是CPU开销下降后同样的机器规格能支撑的QPS上探。我改造过的一个模拟项目X中核心网关在保持P99延迟不变的前提下吞吐提升了60%以上。这意味着在业务增长期可以先不扩机器而是通过技术优化把存量资源用满。技术选型带来的不是纸面上的一点数字变化而是直接作用到成本预算上。6.2 游戏服务器与实时交互场景游戏服务器和实时协作软件是二进制序列化的传统优势领域。这两个场景每秒钟产生成千上万条很小的状态同步消息、动作指令、坐标更新。如果这些消息用JSON传输体积膨胀直接造成带宽跑满延迟上升玩家操作反馈变肉。我了解某个轻量级多人联机Demo的开发者他花了两个晚上把每帧状态同步从JSON切到了自定义紧凑二进制格式结果是客户端和服务器之间每帧的消息体从大约400字节降到了90字节。在同样的网络条件下帧同步的稳定性和玩家体验完全是两个级别。游戏行业对这类方案如此依赖本质上是单位带宽内的信息密度决定体验上限。6.3 IoT网关与嵌入式设备的资源约束IoT设备是另一个典型场景。很多物联网模组的内存以KB为单位计量网络带宽按流量计费每一条上行数据都在燃烧成本。二进制序列化对IoT的意义在于减小每条消息的体积直接降低流量费用和电池消耗。降低解析复杂度MCU级别的芯片也能轻松处理。减少内存峰值避免32KB可用内存的设备处理一条200字节JSON时崩溃。在IoT项目里我倾向于在设备端用MessagePack或CBOR这种轻量级方案因为它们不需要强schema管理设备端实现更加轻便网关侧收到后可以直接解析并转发到后端省去了不少协议转换的开发量。6.4 数据存储与大数据管道的成本账如果业务每天产生几十上百GB的日志或埋点数据序列化格式直接影响存储成本。用JSON存储和用Avro或Protobuf存储同样的信息量体积差距大约在2到3倍之间。这意味着存储费用、备份费用、从存储读取时的网络和计算费用全部按这个比例缩放。大数据生态里Avro特别流行是因为它还自带了schema信息文件可以自描述数据分析工具链条也能直接识别不用额外维护一份外部schema仓库。这类方案在数据湖和数据仓库场景下的价值已经不单单是性能优化而是数据治理的一部分。7. 踩坑记录与个人感悟7.1 一次大规模切换的复盘与教训最后分享一个我经手的比较惨痛的案例。某次切流量切得太激进48小时内把全部客户端推到了新协议结果兼容测试没覆盖某两个老版本客户端。上线当晚这两个版本的客户端全部解析失败服务端报错刷屏。事后复盘问题出在我们只测了新客户端和最新版老客户端的兼容路径忽略了对更老版本的模拟。从那以后我定了几条规矩切换协议必须有向后兼容矩阵每个历史主版本客户端至少要有一条可用的兼容通道灰度比例从1%起步失败率超过0.1%自动熔断并回滚。协议升级不是纯技术问题更是发布工程和风险控制问题心里要牢牢树立这根弦。7.2 在项目中推广二进制序列化的沟通策略换序列化方案经常不是纯技术问题团队里会有人质疑好好的JSON为什么非要换看不见摸不着出了问题怎么排查拿性能数据说话是最有力的方式。把线上的CPU profile、带宽监控、延迟分位数摆出来结合改造前后同一段时间的对比曲线远比口头说“会更快”有说服力。从最痛的地方下手也很重要找一个CPU最高、流量最大的接口先做试点让收益迅速可见。我每次都是这么干的先选一个最痛的接口做试点跑一周数据拿指标对比结果给团队看效果后续推广就顺理成章。反过来上来就铺全量一旦遇到阻力很容易推翻整件事。7.3 什么时候要坚持二进制什么时候果断放弃我观察到有些人走向另一个极端觉得JSON一无是处到处强制二进制化。实际上技术选型永远要回到成本收益分析。如果改造会引入大量维护负担又不解决任何实际瓶颈就没有理由硬换。如果确认瓶颈就在序列化上那就要坚持推进不要被“现在这版本够用”拖住后腿。我个人的判断标准是三步是否确实存在可量化的性能或成本瓶颈现有团队是否具备schema管理和协议工具的维护能力是否有足够的测试覆盖和灰度发布机制来保证平稳切换三个答案都是肯定那就坚决换、利索换。否则宁可等等做好准备工作再动手。踩过几次坑之后我最大的心得是序列化方案本身没有绝对好坏只有适合不适合。而二进制序列化在绝大多数高性能链路上确实值得作为优先考虑的方案之一。7.4 最后一个实用技巧小成本验证一切不论你最终选什么方案都不要在概念阶段浪费太多时间争论。我推荐一个低成本又不损信誉的验证方法拿真实业务里最核心的三个数据结构分别用JSON和选定的二进制方案各写一小段序列化反序列化的benchmark结合线上数据回放跑一遍。这个步骤一天之内就能完成得到的结果足够支撑选型判断。相比看无数篇文章、争论三天理论我更相信亲手跑出来的数字。这套方法论同样可以用在取舍其他技术栈上先把验证成本降到最低再把决策权交给实测数据这样在团队里推动技术演进阻力会比凭空说教小得多。
阅读完成 · 觉得有帮助?