想象你经营一家大型仓储超市,周围几万家客户每天下单。最开始你只有一名快递员:每次只能扛一个包裹,从仓库跑到客户家,再跑回来,才能接下一单。用户一多,门口排起长队,每个订单都要等很久。后来公司升级了物流系统:快递员换成一辆带多个仓位的大货车,一条路线同时配送几十个包裹;包裹上的地址从手写长文本变成扫码即识别的二维码;货车不再每次从总部调派,而是在客户小区设了前置站;道路还装上了智能红绿灯,防止前方仓库爆仓堵死。这就是 gRPC 高性能通信的故事——AI 推理服务就是那座仓库,客户端就是下单的客户,而 gRPC 就是那套现代化的物流系统。这篇文章带你拆解它的四个核心部件:HTTP/2 多路复用、Protobuf 二进制编码、连接池与 keepalive 保活、流控与背压,最后落到 AI 推理服务的批处理优化实战上。

岗位关联

掌握 gRPC 高性能通信原理与优化手法,是 AI 服务工程师的硬核竞争力:同样的 GPU 资源,吞吐可以差出数倍,延迟体验天壤之别。

目录
  1. 可视化演示
  2. 通信基础概览
  3. 多路复用机制
  4. Protobuf 二进制编码
  5. 连接池与 keepalive 管理
  6. 流控与背压保护
  7. AI 推理服务优化实战
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示gRPC三大核心优势:HTTP/2多路复用并发流、Protobuf二进制TLV编码效率、gRPC与REST性能对比数据,切换模式查看不同层面。

本节配有交互动效(进入视口后自动加载,约 3KB)。

HTTP/2多路复用:单连接并发流 一个TCP连接承载多个并发请求响应 HTTP/1.1队头阻塞:慢请求阻塞后续 Multiplexing = 1 TCP × N streams 消除队头阻塞,连接复用率大幅提升。

通信基础概览

回到开头的物流故事。仓库(服务端)和客户(客户端)谈生意,传统方式是你亲自跑一趟柜台:排队、手写单子、等仓库翻找、再拎着货回家。互联网世界里的 HTTP/1.1 几乎就是这样——每次请求都要新建连接、重发大段请求头、独自占用一条路。而 gRPC 给出的升级方案是“电话下单 + 统一配送”:客户端像调用本地函数一样喊一声“帮我算这个”,框架自动把参数打包成小包裹、走最省的高速、到仓库门口拆包递交,全程无需客户操心。

gRPC 全称 gRPC Remote Procedure Call,是 Google 开源的高性能远程过程调用(RPC)框架。RPC 的核心思想是“远程像本地”:客户端写一行函数调用,剩下的网络细节全部由框架代劳。gRPC 默认跑在 HTTP/2 之上,消息用 Protobuf 序列化。想知道它为什么快,第一步是看清一次调用到底把时间花在了哪。

一次 gRPC 调用可以拆成四段耗时:客户端把请求对象序列化成字节、字节在网络上传输、服务端执行业务逻辑、再把响应字节反序列化成对象。REST 接口常用 JSON,字段名、引号、空格反复出现,序列化和传输都不便宜;gRPC 用 Protobuf 二进制,体积小一个量级,再加上 HTTP/2 复用连接,网络往返次数也大幅减少。

四个环节就像“下单—打包—配送—拆包验收”,任何一个环节慢,整个订单就慢。gRPC 的整套设计,正是把每一段都换成更快的作业方式:编码换二进制、运输换多路复用、链路换连接池——下面一节一节拆开看。

Tcost=Tserialize+Tnetwork+Tprocess+Tdeserialize\displaystyle T_{\text{cost}} = T_{\text{serialize}} + T_{\text{network}} + T_{\text{process}} + T_{\text{deserialize}}
一次远程调用的总耗时,等于“打包、上路、干活、拆包”四段时间之和,哪段都可能成为瓶颈。
  1. 对象序列化把内存中的请求对象压成二进制字节的耗时
    TserializeT_{\text{serialize}}
  2. 网络传输字节在链路上赶路,耗时取决于数据体积与往返时延
    Tnetwork=SizeBW+RTTT_{\text{network}} = \frac{Size}{BW} + RTT
  3. 服务端处理服务端执行业务逻辑和模型推理的耗时
    TprocessT_{\text{process}}
  4. 响应反序列化把返回的字节还原成客户端对象的耗时
    TdeserializeT_{\text{deserialize}}

多路复用机制

早期快递公司只有“单座小货车”HTTP/1.1:一辆车一次只能装一个包裹,送完回来才能再装下一单。为了不排队,浏览器最多同时开 6 辆车,可车一多,城市道路反而更堵。HTTP/2 直接换了车型:一辆“多仓位挂车”瞬间能装几十个包裹,沿同一条高速前行,每个包裹各占一个仓位,谁先到谁先卸,互不挡路。

在 HTTP/2 里,这条“高速”就是一条 TCP 连接,上面跑的独立仓位叫作流(Stream),每一条流承载一个请求-响应;流再被拆成更小的帧(Frame),在连接上交错穿插地传输,接收端按帧头里的流 ID 把碎片重新拼起来。因为帧可以穿插,一个慢请求不再拖住后面的快请求——应用层的队头阻塞被消除了。

多路复用的直接收益是省握手:HTTP/1.1 开 N 条连接要做 N 次 TCP 握手和 TLS 握手,HTTP/2 只需要一次。再加上 HPACK 头压缩:请求头里重复的字段只传一次,之后用静态表和动态表里的索引号代替,头部开销几乎归零。下面用 N 个请求的完成时间对比两者的差距。

一车多仓加交错配送,这就是 HTTP/2 并行能力的来源。对 AI 服务来说,高并发推理请求就像高峰期的一堆小包裹,用一辆多仓位货车去拉,吞吐自然天差地别。

T1.1=N×(RTT+Tp),T2=⌈NS⌉×(RTT+Tp)\displaystyle T_{1.1} = N \times (RTT + T_{p}), \qquad T_{2} = \left\lceil \frac{N}{S} \right\rceil \times (RTT + T_{p})
HTTP/1.1 的请求一个接一个排队,总时间是“个数 × 单请求耗时”;HTTP/2 每批并行 S 个,批次变少,总时间大幅缩短。
  1. 请求个数要发出的 RPC 请求总数
    NN
  2. 单请求基本耗时一次网络往返加一次服务处理的时间,是速度的地板
    RTT+TpRTT + T_{p}
  3. 并发流数HTTP/2 一条连接上同时跑的流数量,相当于货车的仓位总数
    SS
  4. 向上取整每 S 个请求一批并行,批次之间只能串行推进
    ⌈NS⌉\left\lceil \frac{N}{S} \right\rceil

Protobuf 二进制编码

快递标签的进化是这个故事的最佳缩影:过去手写一长串地址,字小、易错、贴单慢;如今一张二维码,面积一小块,机器“嘀”一声全知道。Protobuf 就是 gRPC 的二维码——把结构化数据压成紧凑的二进制。

Protobuf 使用接口定义语言(IDL)编写 .proto 文件描述消息结构,再由编译器生成各语言代码。它不传输字段名,只传输字段编号,因此省掉了 JSON 里大量“键名 + 引号 + 逗号”的冗余字符。编码上有两个关键设计:一是每个字段的 key 由字段号左移 3 位再按位或上类型(wire type)得到,二进制里同时携带“哪个字段”和“什么类型”两种信息;二是小整数走 varint 编码,0 到 127 只占 1 个字节,超过 127 才用更多字节,每字节 7 位有效数据、最高位表示“后面还有”。

同样的收件信息,手写长文本(JSON)和二维码(Protobuf),体积可能差 3 到 5 倍。在千万级 QPS 的 AI 服务上,这点差距会放大成显著的带宽、时延和部署成本差异——这也是为什么 AI 服务之间的内部接口几乎都选 gRPC。

n=∑i=0k−1(bi mod 128)×128i\displaystyle n = \sum_{i=0}^{k-1} (b_{i} \bmod 128) \times 128^{i}
varint 把一个整数拆成若干个字节,每个字节只贡献 7 位,从低到高按 128 的幂次累加还原,小数字天然省空间。
  1. 低七位数据每个字节只有低 7 位参与表示数字,最高位是“续传”标志
    bi mod 128,0≤bi mod 128≤127b_{i} \bmod 128,\quad 0 \le b_{i} \bmod 128 \le 127
  2. 权重累进第 i 个字节的 7 位数据按 128 的 i 次方加权,位置越靠后的字节权重越大
    (bi mod 128)×128i(b_{i} \bmod 128) \times 128^{i}
  3. 求和还原把所有字节的加权值加起来,就得到原始整数
    n=∑i=0k−1(bi mod 128)×128in = \sum_{i=0}^{k-1}(b_{i} \bmod 128) \times 128^{i}

连接池与 keepalive 管理

货车每次都从总部调派,路上浪费太多时间。聪明的物流公司会在客户密集的小区设“前置仓”,几辆车常驻门口,来了订单立刻出发。gRPC 的连接池也是这个思路:预先建好几条到服务端的长连接,请求来了直接复用,省掉反复 TCP/TLS 握手的成本。

gRPC 客户端里,Channel 是到服务端的虚拟连接,内部管理者多条真实的 HTTP/2 连接。请求进来时从池里挑一条,通过多路复用塞进空闲流。但长连接有个隐患:服务器或中间网关会回收空闲太久的连接,于是需要 keepalive 机制——定期发 PING 探活,相当于司机每隔一段时间给总部报一声平安,证明这条线路还通着。

keepalive 调参有讲究:keepalive_time 是空闲多久后发探活,keepalive_timeout 是等多久没回音就判定断线,keepalive_permit_without_calls 决定没有活跃请求时是否也保活。间隔太短会浪费带宽,太长会被服务端抢先回收。连接池的利用率,可以用一条连接“干活时间占总生命周期比例”来衡量。

前置仓(连接池)解决“车从哪来”,报平安电话(keepalive)解决“车还活着吗”。两者配合,让绝大部分请求都命中已经热好的链路,延迟自然更低,这也是大规模 AI 推理网关能够稳定支撑高并发的基石之一。

U=∑Tbusy∑Tbusy+∑Tidle+n×Tsetup\displaystyle U = \frac{\sum T_{\text{busy}}}{\sum T_{\text{busy}} + \sum T_{\text{idle}} + n \times T_{\text{setup}}}
利用率 = 真正干活的时间 ÷(干活 + 空闲 + 建连)的总时间。连接越多、每建一次越贵,复用越重要。
  1. 忙时合计连接上真正在传输数据的时间总和
    ∑Tbusy\sum T_{\text{busy}}
  2. 空闲合计连接健在但无事可做的时间总和
    ∑Tidle\sum T_{\text{idle}}
  3. 建连开销每条新连接做 TCP 握手与 TLS 握手消耗的时间
    n×Tsetupn \times T_{\text{setup}}

流控与背压保护

高速路再通畅,如果前方仓库收件口爆满、卸不下货,车还是会堵在门口。物流调度中心的答案是流量管制:仓库一说“暂缓放行”,收费站立刻掐住车流,等仓库腾出地方再恢复。这就是流控(flow control)——它管的是“现在能放多少货过去”。

HTTP/2 的流控是“额度制”:接收方告诉发送方“我还有多少字节的接收能力”——这个数字叫流控窗口。发送方累计发出去的字节数不能超过窗口,窗口用尽就必须暂停;接收方处理完数据后,通过 WINDOW_UPDATE 帧补充额度。连接级和流级各有一套窗口,像高速路既有全路限速,也有分车道管制。

流控的核心价值是背压(backpressure):让生产速率去匹配消费速率,防止接收方内存被堆积的数据撑爆。AI 推理中,流式输出一个长回答可能产生大量数据,如果没有流控,慢消费者会被数据淹没。窗口设多大?经验值是参考带宽延迟积:链路带宽乘以往返时延,正好是“管道里能飞多少数据”。

红绿灯不是添堵,而是让整条路不堵死。流控窗口看似限制了瞬时速度,却保证了长期稳定吞吐——对追求 P99 稳定的 AI 服务来说,这种背压保护比一味冲快更重要。

W=BW×RTT\displaystyle W = \text{BW} \times \text{RTT}
带宽延迟积 = 在途数据上限,也是流控窗口应设置的参考值,让管道既装得满又不至于溢出。
  1. 带宽链路单位时间能传输的字节数,相当于道路的限速
    BW\text{BW}
  2. 往返时延数据在发送端和接收端之间打个来回的时间
    RTT\text{RTT}
  3. 在途上限两者相乘就是一条链路上能“飞行”的数据总量,即窗口大小的参考基准
    W=BW×RTTW = \text{BW} \times \text{RTT}

AI 推理服务优化实战

大促期间,收银台前每个人只买一件小商品,一个个扫码结账,收银员累半死,顾客等半天。聪明的做法是让排队的订单合并成“团购单”一起结算:摊薄开单成本,吞吐立涨。AI 推理的 GPU 就像那个收银员——它天生擅长并行算一大批数据,逐个请求反而浪费算力。

这一节把前面所有零件组装到 AI 推理服务上。第一招是服务端流式(server streaming):大模型生成回答是逐 token 的,让服务端产出一个 token 就推一个,用户立刻看到打字机效果,首 token 延迟大幅下降,不用干等整段生成完。

第二招是动态批处理(dynamic batching):收集窗口内到达的请求,攒够一批再交给 GPU,固定开销被摊薄;但批大小不是越大越好——队列等待会让单请求延迟恶化,需要在吞吐和延迟之间折中。第三招是超时与重试:调用时设 deadline 而不是无限等,重试只对幂等请求做,生成类任务可用对冲请求抢最快返回;第四招是连接池与 keepalive 调优,把链路预热到位。

在一组公开对比中,同样的推理模型,REST + JSON 改造成 gRPC + Protobuf 后,单请求的序列化加传输耗时下降约 60%;再加上动态批处理,单卡吞吐提升 3 到 8 倍;服务端流式让首 token 延迟下降约 70%。数据因场景而异,但方向一致:通信层的每一点优化,都会在吞吐曲线上放大成倍数级收益——收银台合并结算的朴素道理,在 GPU 上被放大了成千上万倍。

Speedup=B×tˉsingletbatch\displaystyle \text{Speedup} = \frac{B \times \bar{t}_{\text{single}}}{t_{\text{batch}}}
加速比 = 串行逐个推理的总耗时 ÷ 批量推理的总耗时。数值越大,说明 GPU 并行收益越可观。
  1. 串行单耗时一个请求单独走完全程的平均时间,是批处理的基线
    tˉsingle\bar{t}_{\text{single}}
  2. 批大小一次合并推理的请求数量,相当于团购单里的订单数
    BB
  3. 批量总耗时整批请求一起算完所用的时间
    tbatcht_{\text{batch}}
  4. 加速比串行总时长除以批量时长,大于 1 说明批处理划算
    Speedup=B×tˉsingletbatch\text{Speedup} = \frac{B \times \bar{t}_{\text{single}}}{t_{\text{batch}}}

AI 应用场景

大模型推理网关的流式加速

阶段:模型服务部署与在线推理

解决的问题:大模型生成回答是逐 token 产出的,如果用 REST + JSON 等完整响应,用户要干等到整段生成完,首 token 延迟高、网络占用大;gRPC 的服务端流式把每个 token 即时推给客户端,配合 Protobuf 压缩体积,打字机体验轻松落地。

具体案例:某智能客服平台在模型前加了一层 gRPC 推理网关,用动态批处理把并发请求合并交给 GPU,再用服务端流式逐 token 回传。改造前后对比,P99 从 900ms 降到 450ms,单机 QPS 提升约 4 倍,GPU 利用率从 40% 升到 85%。

多模型微服务间的链路加速

阶段:服务编排与链路调用

解决的问题:推荐、多模态系统中,召回、粗排、精排等多个服务之间高频互相调用,HTTP/1.1 每次请求都要新建连接、重复握手,链路越长浪费越大;gRPC 连接池让服务间复用少量长连接,keepalive 保活避免连接被中间网关回收。

具体案例:某推荐系统把内部接口从 REST 迁移到 gRPC,服务间数百条短连接收敛为每条服务仅 3 到 5 条长连接,跨服务链路调用 P99 降低约 35%,CPU 平峰占用也明显下降。

Python 演示

python86 行
import timeimport threading  class SharedConnection:    # 模拟一条 HTTP/2 连接, 内部可开多条 stream 并行收发     def __init__(self, name="conn-1"):        self.name = name        self.next_id = 1        self.lock = threading.Lock()     def new_stream(self):        with self.lock:            sid = self.next_id            self.next_id += 2  # 客户端发起的 stream id 从 1 开始奇数递增        return sid  def encode_varint(value):    # Protobuf varint 编码, 小整数只需要 1 个字节    buf = bytearray()    while value > 127:        buf.append((value & 127) | 128)        value >>= 7    buf.append(value)    return bytes(buf)  def encode_field(field_no, value):    # key = (字段号 << 3) | wire_type, 字段号 1, wire_type 0 表示 varint    key = encode_varint((field_no << 3) | 0)    return key + encode_varint(value)  class BatchInferenceService:    # 连接池: 复用少量连接, 对比单请求与批量推理的耗时     def __init__(self):        self.channels = [SharedConnection("conn-%d" % i) for i in range(4)]        self.pool_next = 0     def _pick_channel(self):        ch = self.channels[self.pool_next % len(self.channels)]        self.pool_next += 1        return ch     def infer_single(self, req_id):        conn = self._pick_channel()        stream = conn.new_stream()        time.sleep(0.01)  # 模拟一次网络往返和一次推理        return (req_id, conn.name, stream)     def infer_batch(self, req_ids):        conn = self._pick_channel()        stream = conn.new_stream()        time.sleep(0.02)  # 批量推理只比单个慢一点, 摊薄固定开销        return [(rid, conn.name, stream) for rid in req_ids]  def main():    # 对比 protobuf varint 编码与等价的 JSON 文本体积    body = encode_field(1, 10000)    json_style = len('{"req_id":10000}')    print("proto bytes =", len(body), "json bytes =", json_style)     svc = BatchInferenceService()    batch = 8     t0 = time.time()    for i in range(batch):        svc.infer_single(i)    single_ms = (time.time() - t0) / batch * 1000     t0 = time.time()    svc.infer_batch(list(range(batch)))    batch_ms = (time.time() - t0) * 1000     print("single avg = %.2f ms, batch (%d) = %.2f ms, speedup = %.2fx"          % (single_ms, batch, batch_ms, single_ms * batch / batch_ms))    print("pool size =", len(svc.channels),          "alive =", sum(1 for c in svc.channels))  if __name__ == "__main__":    main()
目的:模拟 gRPC 连接池复用与 Protobuf varint 编码,对比批量推理与单请求推理的吞吐差距。
  1. while value > 127:只要还有超过 7 位的有效数据,就要继续拆分下一个字节
  2. buf.append((value & 127) | 128)截取低 7 位,并置最高位“延续标志”,告诉解码端后面还有字节
  3. key = encode_varint((field_no << 3) | 0)Protobuf 协议规定 key 的高位是字段号、低位是类型,所以整条消息不必传字段名
  4. with self.lock:用线程锁保证多条并发流拿到不重复的 stream id,就像给包裹分配不冲突的仓位号
  5. time.sleep(0.02)模拟批量推理:8 个请求一次算,只比单个慢约一倍,固定开销被摊薄
  6. single_ms * batch / batch_ms批量相对串行的吞吐加速比,体现批处理的核心收益

常见误区

误区我听说 gRPC 快是因为 Protobuf 是二进制,那把 JSON 换成二进制传输格式,效果应该就差不多了吧?

正解性能提升来自三部分叠加:HTTP/2 多路复用省去大量连接建立和排队等待,Protobuf 体积小减少传输字节,HPACK 头压缩消除重复信息。只换格式不换传输层,在高并发场景下收益有限——就像只换快递标签却不换货车,排队问题依旧。

为何常见:二进制格式最直观、最容易归因,但它只是拼图之一;连接复用带来的结构性收益往往藏在看不见的地方。

误区为了高并发,我在客户端和服务端之间开了几百条 gRPC 连接,总觉得多开连接更保险。

正解HTTP/2 多路复用让一条连接就能承载大量并发流,连接过多反而浪费内存、端口和调度开销,甚至触发内核连接数瓶颈。合理做法是少量连接加连接池复用,让每条连接都保持忙碌状态。

为何常见:HTTP/1.1 时代并发靠多连接,浏览器六连接上限的思维惯性让很多人延续了“连接数等于并发度”的旧认知。

误区开了 keepalive 就不怕请求丢了,它应该会自动重发没到的消息吧?

正解keepalive 只是“活性探测”——确认连接还活着,相当于司机报平安,不负责包裹本身是否签收。请求是否送达、要不要重发,由超时时间和重试策略决定,两者职责完全不同。

为何常见:“保活”这个词太有迷惑性,字面理解成“保证存活与送达”,其实它只管“链路没断”这一个指标。

误区流控窗口限制了我发数据的速度,那我把窗口设成无限大,网络吞吐不就上去了?

正解流控是接收方的自我保护机制,窗口过大会导致接收端内存被堆积的数据撑爆。在 AI 长响应场景尤其危险——一个流式输出就能灌满几个 GB 内存。正确做法是按带宽延迟积设置合理窗口,而不是一刀切关掉。

为何常见:只看到“限速”的负面效果,没看到背压是防止系统缓冲堆积、保障 P99 稳定的关键设计。

面试题

Q1. gRPC 默认运行在哪个协议之上?

单选题 · easy

  • A.基于 HTTP/1.1
  • B.基于 HTTP/2← 正确答案
  • C.基于 WebSocket
  • D.基于裸 TCP

答案:B。gRPC 以 HTTP/2 为运输干线,相当于快递公司统一走高架;HTTP/1.1 是旧国道,WebSocket 和裸 TCP 都不是 gRPC 的默认承载。

Q2. HTTP/2 多路复用中,并行传输的独立通道叫什么?

单选题 · easy

  • A.管道 Pipe
  • B.帧 Frame
  • C.流 Stream← 正确答案
  • D.块 Block

答案:C。每条请求是一条流(Stream),相当于货车上的独立仓位;帧是流里更小的数据块。多路复用就是一条连接上多条流并行推进。

Q3. Protobuf 中,整数 300 用 varint 编码需要几个字节?

单选题 · mid

  • A.1 字节
  • B.2 字节← 正确答案
  • C.4 字节
  • D.8 字节

答案:B。varint 每字节只存 7 位数据,300 的二进制占 9 位,所以需要 2 个字节;只有 0 到 127 之间的整数才用 1 字节。这正是小整数省空间的原理。

Q4. gRPC 连接池中,keepalive ping 的主要作用是什么?

单选题 · mid

  • A.加密通道里的秘密暗号
  • B.检测连接是否仍然存活← 正确答案
  • C.定时重新发送请求数据
  • D.对每次消息进行安全校验

答案:B。keepalive ping 相当于司机定时报平安,只探测连接是否还活着,不负责重发消息,更不是安全校验。真正保证请求送达靠的是超时和重试机制。

Q5. HTTP/2 流控中,接收方通过什么帧来补充发送额度?

单选题 · mid

  • A.窗口更新帧 WindowUpdate← 正确答案
  • B.存活探测帧 PingFrame
  • C.设置协商帧 SettingsFrame
  • D.头部附着帧 HeadersFrame

答案:A。接收方处理完数据后发 WINDOW_UPDATE 帧补充额度,相当于仓库清出仓位后通知高速口放行;PING 管存活,SETTINGS 管参数协商,HEADERS 携带请求头。

Q6. 关于 HTTP/2 与 HTTP/1.1 的队头阻塞,下列说法正确的是?

单选题 · hard

  • A.HTTP/2 彻底消除了所有网络层队头阻塞
  • B.HTTP/2 解决应用层阻塞但 TCP 层仍有阻塞← 正确答案
  • C.HTTP/1.1 的六条连接可完全避免排队等待
  • D.HTTP/2 的多路复用只对下载文件场景有效

答案:B。HTTP/2 让请求不用在应用层排队,但底层仍是单条 TCP 连接,一旦丢包重传,TCP 层的队头阻塞依然存在;彻底解决要等 HTTP/3 的 QUIC。

Q7. 为 LLM 流式输出设计 gRPC 接口,最合适的方式是?

单选题 · hard

  • A.一元 RPC 一次性返回全部文本
  • B.客户端流式一次性发送文本数据
  • C.服务端流式逐 token 推送结果← 正确答案
  • D.双向流式同时收发大量文本数据

答案:C。大模型生成是逐 token 产出的,服务端流式 RPC 每产出一点立即推送,首 token 延迟最低、体验最好;一元 RPC 要等全部生成完,已经跟不上流式交互的节奏。

Q8. 以下哪些是 Protobuf 相比 JSON 的优势?(多选)

多选题 · easy

  • A.序列化体积更紧凑← 正确答案
  • B.编解码速度更快← 正确答案
  • C.天然支持字段校验
  • D.解析无需预定义文件

答案:AB。Protobuf 不传字段名只传编号,体积更小、编解码更快;字段校验要靠额外插件而非语言自带,且必须先用 .proto 定义结构,所以 C、D 都错。

Q9. 关于 gRPC 连接池与 keepalive,正确的做法有?(多选)

多选题 · mid

  • A.少量长连接复用代替大量短连接← 正确答案
  • B.keepalive 间隔要小于服务端超时← 正确答案
  • C.空闲连接越多保活效果越好
  • D.连接断开后应立即重连并建立新池

答案:AB。连接池用少量长连接复用即可,像前置仓备几辆车足够;keepalive 间隔若大于服务端空闲回收超时,连接会被抢先回收。空闲连接堆太多浪费资源,重连也应带退避,所以 C、D 错。

Q10. 为 AI 推理服务做 gRPC 性能优化,合理的手段有?(多选)

多选题 · hard

  • A.服务端流式返回逐步生成结果← 正确答案
  • B.动态批处理摊薄 GPU 计算开销← 正确答案
  • C.把流控窗口设成无限大提速
  • D.用 deadline 超时并配合幂等重试← 正确答案

答案:ABD。流式返回提升体验,动态批处理摊薄固定开销,deadline 加幂等重试保障可靠性;但流控窗口设无限大会撑爆接收端内存,背压是保护而非枷锁,所以 C 错。