在云雾缭绕的群山深处,有一个叫云雾镇的小镇。镇上唯一的小卖部老板李大伯,每天要往县城的物流总仓发消息:货架上的酱油只剩三瓶了、明早要进二十斤盐、天气预报说后天降温得补一批棉被。可连接小卖部和县城的,只有一条窄窄的盘山泥路。这条路运力有限(带宽窄),李大伯的柜台电脑老旧得连个游戏都跑不动(设备资源少),他一次只能发一句短短的搭话(消息小而频繁),最怕的是雨季一来山路就塌方(连接不稳定)。 后来,物流公司做了一次大改革:把原先“一车一件、排队等货”的老规矩,换成了“一辆大卡车多格货仓、同时装几十件”的新流程——这就是 HTTP/2 的多路复用。再后来,李大伯干脆和县城签了一份冷链运输合同,用标准尺寸的冷链箱运货、约定好路上随时汇报温度——这便是 gRPC 与 Protobuf。读完这篇文章,你会明白:为什么在 IoT 场景里,HTTP/2 与 gRPC 恰似那辆满载包裹进山的大卡车,又是怎样把“实时上报、双向通话”变成现实的。

岗位关联

对 AI 从业者而言,IoT 是模型数据落地的重要入口;理解 HTTP/2 与 gRPC 能帮你设计高效的设备-云端数据链路,让模型推理前的数据采集不再成为瓶颈。

目录
  1. 可视化演示
  2. IoT通信痛点:泥泞山路上的小卖部
  3. HTTP/2多路复用:一辆卡车装下所有包裹
  4. gRPC核心特性:签下冷链专线合同
  5. Protobuf序列化:标准冷链箱
  6. 流式RPC遥测:冷链车实时报温度
  7. 协议对比选型:看路选车
  8. 设备端工程实现:把专线铺进大山
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示HTTP/2多路复用、gRPC四种流式通信模式及Protobuf序列化压缩对比,交互切换三种核心协议机制。

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

HTTP/2多路复用 单TCP连接承载4路流并发传输 帧级交错消除队头阻塞 带宽利用率 = Σ(stream_i) / link_capacity IoT设备复用连接降低握手开销

IoT通信痛点:泥泞山路上的小卖部

云雾镇的小卖部,就是千千万万个 IoT 设备的缩影。李大伯遇到的第一道坎是“路太窄”:一根网线的带宽只有几 Mbps,一条温度、湿度、电量的小消息只有几十字节,可网络包头的开销却有上百字节,就好比为了送一包盐,摩托车上还要占一半位置放送货单。第二道坎是“店里的电脑太旧”:设备内存按 KB 算,跑不了重型的 JSON 解析和 TLS 握手。第三道坎是“消息碎而密”:传感器每几秒就产生一条数据,频率高、单条又短,HTTP/1.1 的处理方式是为每个请求建立连接、排队等待、返回响应,一条条慢慢走,效率极低。第四道坎是“信号时好时坏”:IoT 网络常断断续续,一次长连接刚建立就断,代价高昂。

把这四道坎翻译成技术语言:IoT 核心痛点包括带宽受限、设备资源受限、消息频繁且短小、连接不稳定。HTTP/1.1 的串行请求模型(一个时刻只能处理一个请求,除非开多条 TCP 连接)和冗余的文本头部,在“小消息高频”场景下造成了巨大的浪费。总耗时公式如下:

Ttotal=n×(Tconn+Treq+Tresp)\displaystyle T_{\text{total}} = n \times (T_{\text{conn}} + T_{\text{req}} + T_{\text{resp}})

1. n(请求个数):一个遥测任务需要发送的消息条数,比如李大伯一天要报 200 条库存信息。

nn

2. T_conn(连接建立耗时):TCP 握手加 TLS 握手的一次性成本,在 IoT 弱网环境下可能高达数百毫秒。

TconnT_{\text{conn}}

3. T_req 与 T_resp(请求与响应耗时):发送数据与等待回包的时间,头部越大,这部分越大。

Treq+TrespT_{\text{req}} + T_{\text{resp}}

路还是那条路,但李大伯明白,得换一种“装货”和“派车”的办法了——这正是 HTTP/2 登场的时刻。

HTTP/2多路复用:一辆卡车装下所有包裹

物流公司的大改革来了:一辆大卡车跑一趟进山,车厢被隔成许多一格格的“货位”,每个货位贴上收货人标签(流的标识),几十件包裹同时挤在一辆车上,互不干扰地到达县城。李大伯再也不必等上一辆车送完酱油,才让盐出发。在 HTTP/1.1 时代,同一时刻一条连接上只能有一个请求在途;HTTP/2 则引入“二进制分帧层”,把每个消息切成一个个小帧,帧上写着“我属于哪个流”。多个流的帧像货位上的包裹一样交错排列、并行前进,这叫多路复用。

此外,HTTP/2 还有两张王牌:HPACK 头部压缩——像把所有包裹单上的重复“发货人”信息抽出来统一登记,只给每个包裹一个编号,大幅缩小头部;流优先级——重要的遥测(如设备告警)可以抢在其他流前面。对于 IoT,第一张王牌让“小消息”不再被大头部队拖累;第二张王牌保证了关键数据不被普通遥测淹没。

当一条连接能同时承载 m 个流的消息,假设总共有 n 条平均大小为 S 的消息,带宽为 B,理想情况下总耗时近似为:

Th2≈n×Sˉm×B\displaystyle T_{\text{h2}} \approx \frac{n \times \bar{S}}{m \times B}

1. n 与 S(消息总数与平均大小):要发送的消息条数与平均每条消息的字节数,乘积为总数据量。

n×Sˉn \times \bar{S}

2. m(并行流数):一条 HTTP/2 连接上同时活跃的流数量,相当于卡车货位的格数。

mm

3. B(带宽):链路每秒能传输的比特数,对应盘山路的运力上限。

BB

值得一提的是,这里的 m 同时受 TCP 窗口、服务器设置与设备内存限制。李大伯的大卡车虽能装货,可也不能无限加车厢——工程上要合理控制并发流的数量。

gRPC核心特性:签下冷链专线合同

改革之后,李大伯觉得跟物流公司“一单一单地谈”还是太费口舌,于是双方签了一份《冷链运输长期合同》:合同里明确规定——什么货、什么包装、什么温度、多久送一次。有了合同,双方不用每次都在电话里确认细节,按合同执行即可。gRPC 就是这份“合同”:它是 Google 开源的高性能远程过程调用(RPC)框架,基础是 HTTP/2,默认用 Protobuf 做消息格式。“合同”本身是 .proto 文件,用 service 声明服务方法,用 message 声明数据结构。

这份合同带来三个关键好处:第一,强类型接口——接口长什么样,编译期就确定,写错字段名直接报错;第二,四种通信模式——一元(一应一答)、服务端流式(云端持续下发)、客户端流式(设备持续上报)、双向流式(两边同时说),覆盖 IoT 绝大多数交互;第三,基于 HTTP/2,天然获得多路复用、头部压缩、连接复用等能力。对比传统 REST 的“无合同、按 URL 传 JSON”,gRPC 更像正规军。

一条 HTTP/2 连接上承载 n 次 gRPC 调用,相比 HTTP/1.1 为每次调用单独建连,节省的连接建立开销为:

ΔC=(n−1)×Ctcp\displaystyle \Delta C = (n - 1) \times C_{\text{tcp}}

1. n(调用次数):一个遥测会话内设备调用云端接口的总次数。

nn

2. C_tcp(单次建连开销):TCP 握手乃至 TLS 握手的耗时与资源消耗,弱网下可能占据单次请求一半时间。

CtcpC_{\text{tcp}}

3. ΔC(省下的开销):复用连接后总共节省的建连成本,请求越多,节省越明显。

ΔC\Delta C

在 IoT 网关到云端的链路上,请求通常每天成千上万次,这条“冷链合同”省下的连接开销,相当可观。

Protobuf序列化:标准冷链箱

合同里最重要的条款,是“统一使用标准冷链箱”。冷链箱尺寸固定、贴编号卡片、没有多余文字。李大伯发货时,把货物装进箱里只需贴上编号:7 号是酱油,8 号是盐。收货员一看编号就知道箱子里装的什么,完全不用读箱子上贴的“这是某某牌农家酿造酱油 500ml 瓶装”之类的长纸条。Protobuf 就是这种“编号箱子”:它为每个字段分配一个编号和类型,序列化时只写“编号 + 值”,而不是像 JSON 那样每个字段都重复写字段名。

举个小例子:JSON 里 {"device_id":7,"temperature":25} 有 30 多个字符;同样的数据用 Protobuf 编码,仅仅 4 个字节:0x08 0x07 0x10 0x19,分别代表“字段 1(device_id)的值是 7”“字段 2(temperature)的值是 25”。体积缩小十倍以上。这背后是 varint——Protobuf 的核心整数编码,小整数只占一个字节,每个字节的最高位用来标记“后面还有没有下一字节”。加字段时只需分配新编号,老客户端遇到未知编号会自动跳过,保留的编号箱子还能继续使用,这就是后向兼容。varint 的数学表达为:

V=∑i=0k(bi mod 128)×128i\displaystyle V = \sum_{i=0}^{k} (b_i \bmod 128) \times 128^{i}

1. b_i(第 i 个字节):varint 编码后的字节序列中的第 i 字节。

bib_i

2. b_i mod 128(取 7 位有效位):把字节的最高位(1 个标记位)去掉,留下 7 个数据位。

bi mod 128b_i \bmod 128

3. 128^i(位权):每后移一个字节,权重乘 128,即 2^7 = 128。

128i128^{i}

正因为 Protobuf 体积小、解析快,IoT 设备那点可怜的内存和带宽,才不至于被冗长的文本协议榨干——冷链箱比贴满标签的纸箱更能装货。

流式RPC遥测:冷链车实时报温度

合同里还有一条:每辆冷链车在路上要实时汇报温度。车上的温度探头每隔几秒就向总部发一条“当前车厢温度 -20℃”(客户端流式);如果哪辆车温度异常偏高,总部立刻通过对讲机喊“把制冷调大”(服务端流式);两边你一句我一句,互不打断(双向流式)。这就是 gRPC 流式通信在 IoT 遥测中的威力。过去用 HTTP/1.1 做遥测,设备每隔几秒就要建立一次连接、传一次头部,浪费且延迟高。

gRPC 在长连接上维护“流”,设备可以在流里连续发送多条遥测消息,云端也可以在同一连接上随时发指令,单条消息的头部被压缩到极小。更重要的是,心跳保活机制让连接长期在线:设备端发送 ping 帧维持 TCP 连接,避免 NAT 超时偷偷掐断。工程上,双向流遥测通常配合消息窗口控制上报频率:比如每秒最多报 10 条。假设每条遥测消息平均 S 字节,每秒上报 f 条,则链路需要的上行速率为:

R=S×f×8\displaystyle R = S \times f \times 8

1. S(消息体大小):一条遥测数据编码后的字节数,Protobuf 能把它压得很小。

SS

2. f(上报频率):每秒发送的遥测条数,IoT 中常见 0.1~10 条/秒。

ff

3. ×8(字节转比特):链路带宽通常以 bit/s 计,1 字节 = 8 比特。

S×f×8S \times f \times 8

代入一个例子:假设 S=32 字节,f=10 条/秒,则 R=2560 bit/s ≈ 2.5 Kbps——在几十 Kbps 的窄带链路上完全可行。这意味着李大伯的冷链车哪怕在信号极差的山路上,也能保证“每十秒报一次温”的实时性。

协议对比选型:看路选车

物流公司把整个运输网络分成了三段:村口泥路——只能进三轮摩托;镇级公路——可以跑轻卡;县城高速——畅通无阻。李大伯的小卖部货物,永远不可能让大卡车直接开进店里卸货;但把货先集中到镇上中转站,再让大卡车上高速,就很合理。IoT 协议选型同理:没有任何一种协议通吃所有场景。

MQTT 就是“三轮摩托”:基于发布-订阅模式,报文极简、功耗极低,适合低功耗传感器直接接入网关;CoAP 是“小船”:基于 UDP,专为极受限设备设计,适合 NB-IoT 等网络;gRPC 是“冷链大卡车”:建立在 HTTP/2 之上,适合设备→网关→云端链路的“中后端”,尤其是需要强类型接口、流式交互、高吞吐的数据通道。选型决策树可以这样看:设备能力极弱、只上报偶发小消息?选 MQTT 或 CoAP。设备由网关代理、数据量大且需要双向实时控制?选 gRPC。链路上行利用率可以由下式估算:

η=SpayloadSpayload+Hheader\displaystyle \eta = \frac{S_{\text{payload}}}{S_{\text{payload}} + H_{\text{header}}}

1. S_payload(有效载荷):真正有用的业务数据字节数,比如温度数值。

SpayloadS_{\text{payload}}

2. H_header(协议开销):传输过程中伴随的头部、字段名、控制信息等非业务字节。

HheaderH_{\text{header}}

3. η(载荷有效率):越接近 1 说明每一比特都用在刀刃上,越接近 0 说明大量带宽被浪费。

η\eta

给李大伯交代一句话总结:进山用摩托(MQTT),出山上高速(gRPC);不要在泥路上硬开大卡车,也不要在高速上慢悠悠骑摩托。

设备端工程实现:把专线铺进大山

合同签了、冷链箱买了,最后一步是把整套流程真正铺进李大伯的小卖部。设备端的 gRPC 工程实现,大致有四件事。第一件:生成代码——用 protoc 编译器把 .proto 合同编译成目标语言代码(Python、C++、Go 等),客户端和服务端都从这套代码开始,保证类型一致;第二件:建立连接(Channel)——创建一个 gRPC Channel 代表与云端的一条 HTTP/2 连接,所有流复用这条 Channel,避免反复建连;第三件:断线重连——IoT 网络不稳定,必须用指数退避加随机抖动决定重连时机,避免成百上千台设备同时重连打爆服务器;第四件:内存优化——复用消息对象、限制发送队列、及时释放流资源,防止内存泄漏。

其中指数退避重连格外重要:第一次断线等 1 秒重试,第二次等 2 秒,第三次 4 秒……呈指数增长,并叠加一个随机抖动防止“惊群”:

Twait=Tmin⁡×2n−1+rand(0,J)\displaystyle T_{\text{wait}} = T_{\min} \times 2^{n-1} + \text{rand}(0, J)

1. T_min(最小退避基数):第一次重试的等待时间,常取 1s 或 500ms。

Tmin⁡T_{\min}

2. n(重试次数):第几次重试,指数体现在 2^(n-1) 上。

2n−12^{n-1}

3. rand(0, J)(随机抖动):在 [0, J] 间取随机值,防止大量设备同时重连造成“惊群效应”。

rand(0,J)\text{rand}(0, J)

当李大伯的店里真正跑起 gRPC:代码由 protoc 生成、Channel 复用、断线自动按指数退避重连、内存使用稳定——他再也不用担心雨季断联了。冷链专线,正式通车。

AI 应用场景

场景1:智能工厂设备温度监控

阶段:数据采集与传输(模型推理的前置环节)

解决的问题:传统工厂用轮询或文件上报方式收集设备温度,延迟高、易丢数。AI 模型需要连续的温度时间序列,gRPC 双向流让边缘网关持续上报温度、云端实时下发控制指令,把数据管道延迟从秒级降到毫秒级。

具体案例:某汽车零部件厂在 200 台注塑机上部署温湿度传感器,设备通过 Modbus 汇聚到边缘网关,网关用 gRPC 双向流把每秒 10 条遥测传入云端推理平台;云端模型发现某台机器温度异常飙升,立刻通过同一个流下发“降速 20%”指令,避免了一次停线事故。

场景2:车联网车队实时调度

阶段:实时特征上报与模型在线决策

解决的问题:车队调度需要实时位置、电量、路况数据。传统 HTTP 轮询请求量大、网络开销高;gRPC 多路复用 + Protobuf 让每辆车一条连接上报多维特征,AI 调度服务实时下发路线调整。

具体案例:某物流公司的 5000 辆新能源货车,每辆车通过车载终端用 gRPC 双向流每 5 秒上报一次位置、电量、载重;云端 AI 调度平台实时预测剩余里程,下发最优充电站与线路,单车通信成本降低 70%。

场景3:智慧农业大棚环境调控

阶段:多传感器数据聚合与闭环控制

解决的问题:大棚内温湿度、光照、CO₂ 传感器数量多、数据密,且需要联动控制。用 gRPC 流式遥测将数据聚合并上报,AI 模型输出调控指令经同一连接下发,做到闭环自动化。

具体案例:某现代农业园区 50 个大棚,每棚 6 类传感器经 LoRa 汇聚到网关,网关 gRPC 客户端 3 秒一次批式上报,AI 平台根据作物模型下发灌溉、通风指令,节水 30%。

Python 演示

python108 行
import randomimport time  # ---------- Protobuf varint 编解码 ----------def varint_encode(value):    """整数编码为 varint 字节串, 小整数仅占 1 字节"""    buf = bytearray()    while value > 0x7F:        buf.append((value & 0x7F) | 0x80)        value >>= 7    buf.append(value)    return bytes(buf)  def varint_decode(data):    """从 varint 字节串还原整数"""    value = 0    shift = 0    for b in data:        value |= (b & 0x7F) << shift        if not (b & 0x80):            break        shift += 7    return value  # ---------- 模拟 protobuf 消息 ----------def encode_telemetry(device_id, temperature):    """field1=device_id, field2=temperature (均用 varint)"""    msg = bytearray()    msg.append(0x08)  # field 1, wire type 0 的 tag    msg.extend(varint_encode(device_id))    msg.append(0x10)  # field 2, wire type 0 的 tag    msg.extend(varint_encode(int(temperature)))    return bytes(msg)  def decode_telemetry(data):    """只解析 varint 字段, 返回 {字段编号: 值}"""    result = {}    i = 0     def read_varint():        nonlocal i        val = 0        shift = 0        while True:            b = data[i]            i += 1            val |= (b & 0x7F) << shift            if not (b & 0x80):                return val            shift += 7     while i < len(data):        tag = data[i]        i += 1        field_no = tag >> 3        result[field_no] = read_varint()    return result  # ---------- 模拟 gRPC 双向流: 设备端 ----------class SmartMeter:    """IoT 智能电表: 持续上报温度(客户端流式)"""     def __init__(self, device_id, interval=0.3):        self.device_id = device_id        self.interval = interval        self.temperature = random.randint(30, 50)     def stream_reports(self, count=10):        """产出 count 条遥测消息"""        for _ in range(count):            self.temperature += random.uniform(-1, 2)            yield encode_telemetry(self.device_id, self.temperature)            time.sleep(self.interval)  # ---------- 模拟 gRPC 双向流: 云端 ----------class CloudServer:    """云端服务: 解析遥测, 发现高温下发指令(服务端流式)"""     def handle_device(self, device_id, report_stream):        """处理一条设备遥测流, 必要时 yield 控制指令"""        for raw in report_stream:            fields = decode_telemetry(raw)            temp = fields.get(2, 0)            if temp > 55:                print("[云端] 设备 {} 温度 {}C 偏高, 下发降温指令".format(fields.get(1), temp))                yield {"action": "cool_down"}            else:                print("[云端] 设备 {} 温度 {}C 正常".format(fields.get(1), temp))            time.sleep(0.1)  def main():    meter = SmartMeter(device_id=7, interval=0.3)    cloud = CloudServer()    for i, command in enumerate(        cloud.handle_device(meter.device_id, meter.stream_reports(count=10))    ):        print("[设备] 收到第 {} 条云端指令: {}".format(i + 1, command))  if __name__ == "__main__":    main()
目的:用纯 Python 模拟 gRPC 双向流遥测的完整链路——设备端用 Protobuf 风格编码持续上报温度,云端解析并视温度情况下发降温指令。
  1. msg.append(0x08)写入“字段 1 且类型为 varint”的 tag,相当于冷链箱上的编号卡片。
  2. buf.append((value & 0x7F) | 0x80)取低 7 位作为有效数据,最高位置 1 表示“后面还有字节”,这是 varint 的核心。
  3. value >>= 7右移 7 位,准备编码下一段,对应公式中的 128^i 位权。
  4. field_no = tag >> 3tag 的高 5 位还原出字段编号,低 3 位是 wire type。
  5. yield encode_telemetry(...)生成器式产出遥测,模拟 gRPC 客户端流式上报。
  6. yield {"action": "cool_down"}云端在流中下发指令,模拟双向流的另一方向。

常见误区

误区我总觉得 gRPC 是给后端微服务用的重型框架,树莓派、ESP32 这种小设备肯定跑不起来,IoT 还是老老实实用 MQTT 吧。

正解gRPC 有多种语言的高性能实现,也有面向嵌入式环境的轻量版本。更常见的架构是“终端传感器跑 MQTT/CoAP,边缘网关跑 gRPC 上云”——就像三轮摩托进村拉货,再在大路换成冷链大卡车上高速。网关级别的设备(树莓派、工业网关)运行 gRPC 毫无压力。

为何常见:gRPC 生态早期主要用于云原生服务发现、服务间调用,教程也多以微服务为例,让人误以为它只属于后端。

误区既然 HTTP/2 支持多个请求并行跑,那网络卡顿的问题不就全都解决了?以后 IoT 上行再也不怕丢包了。

正解HTTP/2 只解决了 HTTP 层的队头阻塞(多个流共享连接),但底层 TCP 有一个包丢包,后续所有帧都会被 TCP 的可靠传输机制挡住,这叫 TCP 层队头阻塞。好比卡车混行在唯一隧道里,最前面一辆车抛锚,后面所有车都过不去。要彻底消除,得上 HTTP/3 的 QUIC。

为何常见:“多路复用”这个名字容易让初学者以为网络传输不再受顺序影响,忽略了它实在 TCP 之上做的优化。

误区proto 文件里字段一旦发布就不能改了,每次加字段都要重新给所有设备发新固件,太麻烦,不如用 JSON 随改随发。

正解Protobuf 的设计天然考虑后向兼容:新增字段用新编号,老客户端会忽略未知字段,新代码也能读到旧数据,只要不修改既有字段编号和类型即可。这就像合同里允许补充新条款,只要不废止旧编号。相比 JSON 运行时才发现字段对不上,Protobuf 的强类型契约反而让升级更可控。

为何常见:习惯于动态类型脚本语言的开发者,容易误以为“改协议”必然“破坏兼容”,忽略了 Protobuf 明文设计好的兼容规则。

误区MQTT 报文头只有几个字节,gRPC 基于 HTTP/2 还是太重,所以 IoT 场景选 MQTT 就对了。

正解要看数据特征。设备偶发小消息、深睡低功耗,MQTT 确实最优;但当数据是连续流、需要双向实时控制、且设备端是网关时,gRPC 的流式模型和 Protobuf 的高吞吐反而更省带宽。三轮摩托和冷链大卡车各有各的路段。

为何常见:对比协议时只比“头部大小”这一个指标,忽略了流式交互、强类型、多路复用等其他维度。

面试题

Q1. HTTP/1.1 在 IoT 高频小消息场景下的主要问题是什么?

单选题 · easy

  • A.支持多路复用但部署复杂
  • B.请求串行且头部开销大← 正确答案
  • C.无法传输任何二进制数据
  • D.强制使用 TLS 加密通信

答案:B。HTTP/1.1 在一个 TCP 连接上同一时刻只能处理一个请求,请求必须排队执行(串行),且文本型头部重复冗长。用小卖部比喻:每送一件货都要单独跑一辆车、填一堆单子。选项 A 不对,HTTP/1.1 本身不支持多路复用;C 不对,HTTP/1.1 可以传二进制;D 不对,HTTP/1.1 不强制 TLS。

Q2. HTTP/2 实现多路复用的关键机制是什么?

单选题 · easy

  • A.引入二进制分帧层← 正确答案
  • B.用 UDP 替代了 TCP
  • C.取消请求响应模型
  • D.强制压缩所有业务数据

答案:A。HTTP/2 在应用层与 TCP 之间加入二进制分帧层,把消息切成带“流 ID”的小帧交错传输,实现一条连接上的多路复用。B 错误,HTTP/2 仍基于 TCP;C 错误,请求-响应模型保留;D 错误,HTTP 不做业务数据强制压缩。

Q3. 下列关于 gRPC 的说法,哪个是正确的?

单选题 · easy

  • A.默认基于 HTTP/2 且使用 Protobuf← 正确答案
  • B.只能用于服务器之间的通信
  • C.只支持 JSON 一种数据格式
  • D.不支持任何形式的流式通信

答案:A。gRPC 是 Google 开源的高性能 RPC 框架,默认传输协议是 HTTP/2、默认序列化格式是 Protobuf。B 错误,gRPC 也用于设备与云端通信;C 错误,gRPC 生态也可用 JSON(如 grpc-gateway)等替代;D 错误,gRPC 支持四种流式模式。

Q4. 一条遥测消息用 Protobuf 编码为 4 字节,而 JSON 文本为 36 字节。在带宽受限的链路上,gRPC 相比 JSON over HTTP/1.1 的主要优势是什么?

单选题 · mid

  • A.无需建立任何网络连接
  • B.自动兼容所有老旧设备
  • C.消息体积更小且支持多路复用← 正确答案
  • D.可无限增大消息不丢包

答案:C。Protobuf 二进制格式让消息体积大幅缩小,HTTP/2 多路复用让一条连接同时承载多个流,两者叠加大大降低带宽占用与延迟。A 错误,gRPC 需要网络连接;B、D 与 gRPC 的特性不相关。

Q5. Protobuf 的 varint 编码中,整数 15 占用多少个字节?

单选题 · mid

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

答案:D。varint 用每个字节的 7 位存数据,最高位只做“是否继续”标记。15 < 128,所以只需要 1 字节。这就像标准冷链箱:小东西用最小格,不为小货物空出大空间。

Q6. gRPC 的四种通信模式包括以下哪些?

多选题 · mid

  • A.一元请求响应模式← 正确答案
  • B.服务端流式模式← 正确答案
  • C.广播总线模式
  • D.双向流式模式← 正确答案

答案:ABD。gRPC 支持一元(Unary)、服务端流式(Server Streaming)、客户端流式(Client Streaming)、双向流式(Bidirectional Streaming)四种模式。广播总线不是 gRPC 内置特性,故 C 错误。

Q7. HTTP/2 的多路复用虽然并行,但仍有队头阻塞隐患。其原因是什么?

单选题 · mid

  • A.底层 TCP 丢包时会阻塞后续帧← 正确答案
  • B.服务器必须按顺序处理请求
  • C.浏览器最多只能开 6 条连接
  • D.头部压缩算法是串行的

答案:A。多路复用解决了 HTTP 层的队头阻塞,但底层 TCP 一旦丢包,丢失点之后的帧都要等待重传,这被称为 TCP 层队头阻塞。就像卡车排队过隧道,最前面一辆抛锚,后面全部堵住。

Q8. 设备端 gRPC 断线重连的工程实践包括以下哪些?

多选题 · hard

  • A.采用指数退避重连算法← 正确答案
  • B.加入随机抖动避免惊群← 正确答案
  • C.每次重连都新建 Channel
  • D.退避时间设置一个上限← 正确答案

答案:ABD。指数退避让重试间隔 1s、2s、4s……增长,随机抖动防止大批设备同时重连冲击服务器,退避上限避免无限等待。Channel 应当复用而不是每次重连新建,故 C 错误。

Q9. 边缘网关与云端之间需要持续上报遥测并接收指令,最合适的 gRPC 模式是哪种?

单选题 · hard

  • A.一元请求响应模式
  • B.服务端流式模式
  • C.广播总线模式
  • D.双向流式模式← 正确答案

答案:D。双向流式模式让设备持续上报、云端随时下发,两者共用同一条流,延迟最低、连接开销最小。一元模式适合偶尔一问一答,服务端流式适合纯下发,广播模式不是 gRPC 内建能力。

Q10. 相比 MQTT,边缘网关选 gRPC 上报大流量遥测的原因有哪些?

多选题 · hard

  • A.Protobuf 压缩消息体积← 正确答案
  • B.一条 HTTP/2 承载多路流← 正确答案
  • C.强类型接口减少联调错误← 正确答案
  • D.MQTT 无法传输任何数据

答案:ABC。gRPC 在大流量场景胜在三点:Protobuf 体积小、HTTP/2 多路复用承载多条流、强类型接口让前后端契约更可靠。D 明显错误,MQTT 可以传输数据,只是模型与适用场景不同。