CoAP Observe 模式与长轮询:IoT 场景下服务端推送方案深入对比

本文深入对比 CoAP Observe 模式与长轮询在 IoT 服务端推送场景下的机制差异、消息开销、NAT 适配和连接模型,剖析重订阅、挂起请求、中间层缓存等工程坑点,并给出基于链路、业务和团队约束的选型建议,适合物联网平台后端与嵌入式开发者参考。

为什么 IoT 场景需要重新考虑“服务端推送”

做物联网平台的同学应该都有这种体会:设备端的“服务端推送”和互联网后端完全是两回事。Web 端挂一个 WebSocket 或者 SSE,体验已经很成熟,但到了 NB-IoT、LoRaWAN、6LoWPAN 这类链路,带宽按 kbps 计,设备内存按 KB 算,电量和流量都是成本。这时候再谈“长连接”,就需要重新思考整个通信模型。

AI technology illustration

很多团队最初用的是轮询,简单但费电。后来发现服务端要主动通知设备改配置、下发指令,轮询的实时性又不够,于是开始了解 CoAP 的 Observe 模式和长轮询(Long Polling)。这两个词经常被放在一起比较,但它们的底层思路差别其实非常大。这篇文章把两者的机制、成本和适用边界拆开来讲,希望能帮你在具体项目里做选择。

先厘清一个容易混淆的点:CoAP 本身是类似 HTTP 的请求/响应协议,Observe 是它的一个重要扩展,把“请求/响应”变成“订阅/通知”;而长轮询是 HTTP 时代的古老技巧,本质上仍然是请求/响应,只是服务端把响应“挂住”一段时间。一个通常建在 UDP/DTLS 上,一个建在 TCP/TLS 上;一个是要改变通信模型,一个是在原有模型里做延时优化。放在一起比较,不是为了分出高下,而是因为它们经常是 IoT 设备接入云端时的两个候选项。

CoAP Observe 到底做了什么

CoAP 协议本身很轻,头部只有 4 字节左右,基于 UDP,自带确认和重传(CON/NON),在高延迟链路上也能工作。Observe 扩展在 RFC 7641 中定义,思路是这样的:客户端在 GET 请求里带上 Observe: 0 选项,服务端如果支持,会在响应里返回 Observe 选项和一个序列号。从这一刻起,只要资源发生变化,服务端就会主动给客户端发送新的响应。客户端不需要反复询问,只需要维护这个订阅关系。

# 客户端发起 Observe 订阅
GET /temperature
Observe: 0

# 服务端确认订阅,并携带序列号
2.05 Content
Observe: 5
Max-Age: 60
{"value": 23.5}

# 温度变化后,服务端主动推送
2.05 Content
Observe: 9
{"value": 24.1}

这里的几个细节决定了它的行为。序列号是递增的,客户端靠它判断通知的先后顺序,乱序到达时能识别出来;Max-Age 告诉客户端这个订阅在多久之后需要重新注册;如果网络断开、NAT 映射失效、或者设备休眠时间超过订阅寿命,客户端就得重新发一次带 Observe 的 GET。订阅关系是异步的,服务端可能同时维护成千上万个观察者,资源变化时对每个观察者发送通知,但它不会等每个通知都被确认才继续处理下一个。

工程里真正的体验差异在于:Observe 通知的体积通常很小,一个资源变化可能只有几十字节,非常适合低频但会变化的状态上报、配置更新这类业务。但如果一个资源被大量设备订阅,服务端要承担通知扇出的压力;设备侧也要处理通知乱序、重复和丢失。它的低成本是有前提的,前提是你愿意把重传、超时、重新订阅这些逻辑自己管起来。

长轮询在 IoT 语境下长什么样

长轮询不是一种协议,而是一种交互模式。客户端向服务端发出一个 HTTP 请求,服务端不立刻返回,而是把请求挂起,等到有数据可推或者超时了才返回。客户端拿到响应后,立刻再发起一个同样的请求,形成一条不断续上的“准长连接”。

while True:
    resp = GET('/updates?cursor=abc', timeout=75)
    if resp.status == 200 and resp.body.events:
        process(resp.body.events)
        cursor = resp.body.next_cursor
    else:
        # 超时、空响应或连接断开,稍作等待后继续下一轮
        sleep(0.5)

在 IoT 场景里,长轮询最常见的形态是设备通过蜂窝网络或 WiFi 连接到云端 API,周期性地拉取指令或上报事件。好处很实在:不要求设备保持一个长连接,只要请求发出去之后服务端能及时响应,消息就能以接近实时的方式到达;协议栈是现成的,HTTP 客户端到处都有,云平台已有的负载均衡、TLS 终止、日志监控体系都可以直接复用。

代价同样明显。HTTP 头对 IoT 链路来说很重,一个完整请求加响应往往上千字节,实际业务数据可能只有几十字节;TCP 三次握手和 TLS 握手在弱网下会带来额外时延和重连成本;当大量设备同时发起长轮询时,服务端需要同时挂起大量请求,这会直接影响网关的连接数上限和线程模型。

我见过的不少团队选长轮询,不是因为它在技术上最优,而是因为“先让它跑起来”更重要:固件里已经有一套 HTTP 客户端,后端也是现成的 Web 技术栈,不用给运维解释新协议。这种务实没问题,但要清楚自己的代价是什么。

一张表看明白两者的关键差异

下面这张表从工程角度对比两者:

对比维度 CoAP Observe 长轮询
传输层 UDP/DTLS TCP/TLS
单次消息开销 CoAP 头约 4 字节,通知体很小 HTTP 头加 TCP 握手,开销大
推送及时性 资源变化后异步通知 服务端挂起请求,有数据立即返回
连接模型 维护订阅关系,服务端扇出通知 服务端需同时挂起大量请求
NAT 与休眠 依赖周期性重订阅刷新,失效会断通知 每次请求都刷新 NAT 映射,天然保活
故障恢复 CoAP 重传加重订阅 客户端超时后重连、重发请求
适用链路 NB-IoT、6LoWPAN 等窄带低功耗链路 WiFi、4G 等带宽相对宽松的链路

这张表里最容易误导人的是“推送及时性”这一行。Observe 的服务端主动通知看起来实时性更强,但前提是订阅没有被中断;长轮询的请求挂起后,数据一产生就返回,实用实时性其实也很好。真正的差异不在毫秒级,而在链路开销、连接数和运维复杂度的长期累积上。

三个容易翻车的细节

Observe 的重订阅经常被忽略

Observe 不是一次订阅终身有效。Max-Age 到期、设备休眠、网络切换都会导致订阅失效。很多团队上线头两周没事,后来设备开始收不到通知,排查半天发现是设备睡眠唤醒后没有重新发起 Observe 请求。解决办法是在客户端把订阅状态持久化,唤醒后对比服务器时间,超过 Max-Age 就重新订阅;同时,重订阅的时机要加一点随机抖动,否则大量设备同时唤醒会形成通知风暴。

长轮询的“挂起请求”不是免费的

一个长轮询客户端发起请求后,服务端的网关、应用服务器、连接池里都要为它保留对应的资源。设备量从几千涨到几万时,最先崩的往往不是业务逻辑,而是负载均衡的空闲超时、应用服务器的线程数这类基础设施。长轮询不是不能用,而是要在架构上给“挂起”留出专门的处理通道,把等待响应的请求和普通同步请求分开,避免把工作线程占满。

中间层缓存会悄悄破坏推送语义

无论是 CoAP 还是 HTTP,设备接入云端时几乎都会经过网关、代理。HTTP 代理如果缓存了响应,设备拿到的可能是过期数据;CoAP 代理如果缓存了通知,订阅者同样会收到旧值。做推送方案时要显式关闭相关资源的缓存,或者借助 ETag、Observe 序列号让设备判断数据是否新鲜,而不是盲目信任中间层。

怎么选:链路、业务与团队约束一起看

“哪个更好”是个伪命题,答案永远是“看你的约束”。我建议按下面三个层次来判断:

  • 先看链路:NB-IoT、LoRaWAN、6LoWPAN 这类窄带低功耗链路,CoAP Observe 几乎是唯一现实的选择,HTTP 的头部和 TCP 成本在这种链路上不可接受;WiFi、4G 链路相对稳定,长轮询更容易落地。
  • 再看业务:核心诉求是“状态变化尽快到达服务端”(阈值告警、设备上下线),Observe 的事件驱动更自然;核心诉求是“设备定期取回指令”(电表抄表、门锁拉取开锁指令),长轮询的请求/响应模型更简单,也更容易排查。
  • 最后看团队:团队熟悉 REST 和 HTTP 排查工具,云平台也只暴露 HTTP 接口,就不要为了“先进性”强行引入 CoAP;反过来,如果设备管理链路已经在用 CoAP,Observe 是顺手就能拿到的能力,不需要另起炉灶。

如果从零落地,我的建议是这样

先做最小链路验证,不要一开始就追求完整方案。用 CoAP,可以先在服务端实现一个观察者列表,资源变化时做一次扇出,客户端用 coap-client 验证订阅、收通知、重订阅的完整流程,确认 NAT 映射失效后设备能自己恢复。用长轮询,先写一个简单的客户端反复请求一个延迟返回的接口,确认服务端能同时挂起几百个请求不崩,再考虑集群部署。

有几个参数值得提前定下来。CoAP 侧,Max-Age 一般设置 60 到 120 秒,重订阅加随机延时,同时监控通知序列号的跳变和重复。长轮询侧,服务端挂起时间控制在 30 到 60 秒之间,客户端超时设得比服务端稍长(比如 75 秒),防止服务端先超时返回而客户端已经断开;客户端处理错误时要区分超时(继续轮询)和永久失败(退避后重试)。

还有一个兜底建议:不管选哪种,都要保留周期性的普通 GET/POST 作为健康检查和事件回放通道。Observe 中断、长轮询挂不进队列的时候,这个兜底通道能避免设备长期失联。

收尾

CoAP Observe 是一种优雅的异步订阅机制,适合低功耗、窄带宽、有服务端维护订阅关系的场景;长轮询是一种务实的延迟响应技巧,适合 HTTP 技术栈、中等规模、链路相对宽松的场景。两者解决的推送问题有交集,但代价和边界完全不同。

选型不是选最先进的,而是选最匹配自己约束的。把链路成本、设备功耗、团队能长期维护的复杂度这三件事想清楚,答案通常就出来了。

原创文章,作者:fudengji,如若转载,请注明出处:https://fudengji.cn/article/1018/

(0)
上一篇 8小时前
下一篇 45分钟前

相关推荐