3. Observe扩展机制原理

CoAP 的基础交互模型是请求/响应:客户端发一次 GET,服务器回一次 2.05 Content,事务即结束。这种模型对“读一次当前值”足够,但对温度曲线、设备状态、告警标志这类会持续变化的数据,客户端只能靠反复轮询,既浪费带宽又增加时延。Observe 扩展(RFC 7641)在 CoAP 之上引入观察者模式,让客户端注册一次,服务器在资源状态变化时主动推送通知,从而把“拉”变成“推”。

观察者模式如何映射到 CoAP 报文

观察者模式有三个角色:观察者(Observer,即 CoAP 客户端)、被观察者(Observable,即服务器上的某个资源)、以及连接两者的订阅关系。CoAP 用两个机制承载它:一是请求中的 Observe 选项,二是响应中的 Observe 选项。前者表示“我要订阅”,后者表示“这是订阅确认或后续通知”。

Observe 选项编号为 0x06,属于 elective(可选)选项,可出现在请求和响应中。它的取值是一个不超过 3 字节的无符号整数,语义随出现位置不同而不同:

  • 在 GET 请求中,Observe 取值为 0,表示客户端请求注册观察关系;取值为 1,表示客户端请求注销该观察关系。
  • 在 2.05 Content 响应中,Observe 携带一个 24 位序列号,用于让客户端判断通知的新旧顺序。
  • 在错误响应(如 4.04、4.05)中,Observe 选项不应出现,客户端据此判定注册失败。

因此,一个典型的注册请求形如:

CON GET /sensors/temp
  Uri-Path: sensors
  Uri-Path: temp
  Observe: 0
  Token: 0x1a2b

服务器若接受订阅,返回:

ACK 2.05 Content
  Observe: 12
  Token: 0x1a2b
  Content-Format: application/json
  Payload: {"t":23.5}

此后每当温度变化,服务器主动发送新的 2.05 Content,携带递增的 Observe 序列号,Token 与注册时一致,客户端据此把通知关联到对应资源。

通知与确认流程

Observe 的通知可以走 CON(可确认)或 NON(不可确认)两种消息类型,选择取决于可靠性需求与网络开销的权衡。

使用 CON 通知时,服务器发送 CON 消息,客户端必须回 ACK。若服务器在重传超时内未收到 ACK,会重传,直到达到最大重传次数后认为该观察关系失效。这种方式可靠,但每条通知都要一次往返确认,适合告警等关键数据。

使用 NON 通知时,服务器直接发送 NON 消息,客户端不回 ACK。开销小、时延低,但丢包后客户端不会立即知道,只能靠后续通知的序列号发现跳变。适合高频、可容忍偶发丢失的采样数据。

序列号是通知排序的核心。它是一个 24 位整数,回绕后从 0 重新开始。客户端收到通知时,用 RFC 7641 定义的比较规则判断新旧:若新序列号与旧序列号之差(按 24 位模运算)落在 (0, 2^23) 区间,则新通知更新;否则视为过期或重排,应丢弃。这样即使网络乱序,客户端也不会用旧数据覆盖新数据。

此外,客户端在收到通知后若发现资源已不再需要,可发送带 Observe:1 的 GET 注销;服务器也可因资源被删除、权限变更或内部错误,用不带 Observe 选项的响应终止观察关系。

观察关系的生命周期

观察关系不是永久连接,而是一个有明确状态迁移的过程。理解生命周期,才能正确处理超时、注销和异常。

生命周期大致分为四个阶段:注册、活跃、终止、清理。注册阶段客户端发 Observe:0,服务器返回带 Observe 序列号的 2.05 即建立成功。活跃阶段服务器按资源变化推送通知,客户端持续用序列号校验。终止可由任一方发起:客户端发 Observe:1 注销,或服务器返回不带 Observe 的响应。清理阶段双方释放 Token 与观察条目,避免资源泄漏。

需要特别注意的是,观察关系与底层传输绑定。若客户端与服务器之间使用 DTLS,观察关系随 DTLS 会话存在;会话关闭,观察关系即失效。在 NAT 环境中,长时间无通知可能导致 NAT 映射超时,因此服务器通常会设置一个最大通知间隔,即使资源无变化也发送一次“心跳”通知,维持映射与观察关系。

工程建议:为每个观察关系设置合理的 Max-Age 与服务器端最大静默间隔。若资源变化频率远低于 Max-Age,客户端可能误判数据过期;若远高于,则通知风暴会挤占带宽。通常把通知间隔下限设为 1~5 秒,并对高频资源做服务端聚合。

观察关系状态迁移图

下图把上述注册、通知、确认、注销与失效路径集中呈现,便于对照报文理解状态变化。

CoAP Observe 观察关系生命周期 客户端 Observer 服务器 Observable GET Observe:0 + Token 2.05 Content Observe:12 观察关系活跃 序列号递增 · 通知推送 CON/NON 通知(Observe 序列号 +1) ACK(仅 CON 通知需要) GET Observe:1(客户端注销) 2.05 无 Observe(服务器终止) 注册中 等待 2.05 已建立 维护观察条目 观察关系终止 · 释放 Token

结合图示可以看到,注册与终止是两条对称的路径,而活跃阶段的核心是“通知—序列号校验—按需确认”的循环。工程实现中,客户端应维护一张观察表,键为 Token,值为资源路径、最近序列号、最近接收时间;服务器端则维护订阅者列表,并在资源变更时遍历列表推送。只有把生命周期管理做扎实,Observe 才能在低功耗、弱网环境下稳定工作。