摘要

  • 在 RFC 6665 中,Subscription-State: terminated 的对象是订阅,而不是订阅所监测的资源。
  • deactivated、timeout 与 invariant 分别指向迁移、刷新失效与可预见的不变状态;只有 noresource 明确表示受监测的资源状态不复存在。
  • 可靠的证据记录应分别保存订阅生命周期、资源状态正文、NOTIFY 事务、事件包解释以及随后的人或自动化操作。

一个红灯压扁了两套状态机

“terminated”很容易诱发语法上的误判。界面把它摆在设备、账户、呼叫或地址旁边,读者便自然认为旁边的对象被终止了。然而 RFC 6665 给这个词指定的主语是订阅:收到该值的订阅者必须将这项订阅视为终止。这是确定而有用的结论,但范围很窄。

资源本身运行在另一套状态机中。其含义由具体事件包定义:正文是完整状态还是增量状态、空正文意味着什么、什么值算中性状态,都不能由通用 SIP 事件框架替各类应用统一决定。监测关系可以断裂,而对象仍然正常;对象也可能已经消失,而订阅的最终通知仍在途中。

因此,数据库若只留一个“已结束”布尔值,就无法区分观测失败与被观测对象变化。它会让证据链看起来整齐,却删除了后续判断最需要的边界。

七个原因通向不同的未来

RFC 6665 赋予终止原因明确的操作含义。deactivated 要求客户端立即重新订阅,其中一个主要用途是通知方节点迁移;probation 表示稍后再试;rejected 指向授权策略变化,并建议不要重试;timeout 表示订阅未能在到期前刷新,但允许立即重新建立;giveup 表示通知方未能及时取得授权。

最容易混淆的两个原因恰好把边界说明得最清楚。noresource 表示所监测的资源状态已经不存在;invariant 表示该状态在可预见的将来保证不会变化。两者都不鼓励再次订阅,但一个说“没有了”,另一个说“仍在且不变”。把它们写成同一个终局,等于主动丢掉协议提供的信息。

原因还可能缺失或无法识别。此时客户端可结合 retry-after 再次尝试,但观察者不能擅自补成 noresource。终止状态附带的 expires 也不能填补空白;规范明确规定它在这里没有语义,必须忽略。

最后一条通知未必给出最后一个资源值

取消订阅使用 Expires: 0 的 SUBSCRIBE。成功取消会触发最终 NOTIFY,不过 RFC 6665 特别提醒:这个通知可能包含资源状态,也可能不包含。订阅者必须能够处理两种情况。

若正文为空,系统知道的是关系结束,而不是资源最后处于什么值。若正文存在,仍须依据其媒体类型、事件包规则、完整或部分状态语义以及版本信息来解释。头字段不能替属于另一个规范的正文赋义。

对 NOTIFY 返回 200 的意义也同样有限。只要通知对订阅者而言可以接受,就应尽快响应,事务不得为等待用户操作而保持开放。因此 200 证明的是自动化 SIP 元素已可接受地处理请求,并非阅读回执、操作员确认,更不是下游流程已经完成的凭证。

建立订阅也可能以对话外观上的逆序抵达

第一条 NOTIFY 才确认订阅已建立。SUBSCRIBE 的 2xx 响应表示请求获接受并将立即发送通知,但由于消息可能重排、丢失或分叉,NOTIFY 甚至可能先于 SUBSCRIBE 事务完成。第一条通知到达之前,资源应按事件包规定的中性状态处理。

这至少产生三只时钟:事务时钟记录请求与响应,订阅时钟记录建立、刷新、到期和终止,资源时钟记录事件包描述的状态变化。接收、解析并展示通知的观察系统又带来第四只时钟。若仅按抓包时间排序并把最后一项叫作“真相”,因果关系可能被倒置。

可审计记录应保存本地订阅键、Call-ID 与标签、Event 值、目标、NOTIFY CSeq、完整状态头、原因、重试间隔、接收时间、认证结果和响应。另一组字段则记录正文是否存在、媒体类型与摘要、事件包及解析器版本、完整或部分状态属性,以及应用最终推导出的值。

一项 usage 可以终止,而 dialog 仍继续

RFC 6665 把订阅定义为与 dialog 相关联的应用状态。Robert Sparks 的 RFC 5057 解释了为何“相关联”不能被误读为“同一个东西”:多个 dialog usage 可以共享 dialog 状态,却各有独立生命周期。例如转接场景中,invite usage 与 subscription usage 可以同处一个 dialog;订阅结束时,invite usage 仍可能存在。

后来的规范进一步收紧了控制面。RFC 7621 澄清 GRUU 的用法,使请求能指向预期的用户代理实例。由 Sparks 与 Roach 合著的 RFC 7647 处理 REFER 隐式创建订阅以及 dialog 重用所带来的问题。更准确的端点定位和更清楚的 dialog 结构能降低“哪项 usage 产生了事件”的歧义,却不会把 terminated 扩写成资源声明。

所以“会话结束”一类标签也不够严谨。订阅、dialog、dialog usage、呼叫、通知方实例和事件包定义的资源彼此相关,但并非同义词。它们需要各自的标识与后继状态。

Roach 留下的是边界清楚的框架

RFC 6665 于 2012 年 7 月以标准轨文档发布,取代 RFC 3265;两份文档的唯一署名作者都是 Adam Roach。IETF 当前人物页记录他自 1998 年参与 IETF,2017 至 2020 年任 Applications and Real-Time Area Director,曾主持 XCON、SIPCORE 与 NETVC,并列出 23 份 RFC;截至 2026 年 4 月 22 日没有在任角色。

这些事实证明标准贡献,却不证明他控制任何具体部署。产品如何映射原因、设置计时器或命名状态,责任仍在产品与运营者。Roach 的姓名为框架提供出处,不能替实现背书,也不能把订阅证据升级为资源证据。

一份证据包应分别回答五个问题

订阅身份:是哪一个 dialog、事件包、目标与本地键?生命周期:观察到哪个状态、原因、到期规则与接收时间?资源状态:是否有正文,由什么包解释,摘要和版本是什么?传输保管:来自哪个通知方,认证、事务与响应如何?后续行动:系统是重试、等待、迁移、告警还是结案,又依据谁的规则?

分开记录不会削弱自动化,反而让自动化更精确。timeout 可以触发“监测缺口”事件,而不宣称资源死亡;deactivated 可以一直处于待定,直到后继订阅收到第一条 NOTIFY;noresource 可以在不可逆业务动作前要求应用层佐证;缺失原因则应诚实显示为未知。

运营者确实需要一个紧凑状态。最好的紧凑状态不是伪造确定性,而是准确概括协议已经证明的内容:订阅结束,原因已知或未知,资源证据存在或缺失。红色可以继续使用,前提是图例说真话。

来源