摘要
- 新消息替换待发旧消息,不会自动取消旧消息已经触发的展示或业务动作。队列里的变化与接收端的事实需要分开处理。
- Topic 只在同一订阅内关联消息,不替业务判断两条内容是否可以相互替代;替换还会一并改变保留期限、紧急程度和回执订阅。
- 减少通知能节省通信和注意力,但如果被覆盖的内容没有其他可靠来源,节省的投递成本可能变成更高的恢复成本。
一条工作分派已经离开推送服务,正在发往手机。此时业务状态改变,服务器发出替代通知,并成功更新了等待队列。负责发送的一方看到的是旧消息资源消失,使用手机的人却可能先收到旧分派,甚至已经开始处理。
这不是本文发现的一次企业事故,而是消息替换机制本身允许出现的时序。它提醒管理者:能够改写“还在等什么”,并不等于能够改写“别人已经知道什么”。如果产品承诺的是撤回指令,只有队列替换还不够。
2016 年 12 月发布的 RFC 8030 为 Web Push 定义了这样的替换能力。发送方给消息指定 Topic 后,新消息可以替换同一订阅中具有相同 Topic、仍未结束的旧消息。服务创建新的消息资源,同时删除匹配的旧资源。规范也明确考虑了另一种情况:旧消息已经尝试投递,确认却在替换之后才到达。
因此,“新内容已经接管队列”不能被写成“旧内容从未到达”。如果接收端已经显示了信息或执行了动作,替换操作本身不会倒转这些结果。需要撤回、补偿或拒绝过期指令的应用,必须另行定义相应行为。
先问能不能省,再问怎样合并
消息替换的价值并不小。设备暂时离线时,业务对象可能改变多次。恢复连接后,如果每次变化都变成一条提醒,用户会收到一串已经过时的通知,设备也要为这些内容付出接收和处理成本。只保留足以继续工作的最新信息,往往更合理。
但“足以继续工作”是一项业务判断。同样是库存变化,一条消息可能给出目前的完整数量,也可能要求接收方在先前数量上减去一个差额。后一种设计若遗漏中间变化,最终结果未必还能计算正确。再换一种设计,通知只要求应用重新读取库存,那么能否合并就取决于原始数据是否仍然可读,以及一次读取能否满足当前任务。
这些例子不是对具体库存系统的评价,而是在区分三种常被放进同一通知通道的东西:状态快照、必须累计的变化,以及要求重新获取状态的提示。它们看起来都是小消息,却承担着不同的信息责任。
Topic 不认识这种差别。它负责在规定范围内匹配一个值,而不是分析消息中的业务关系。把多项独立工作都放到一个笼统的 Topic 下,可能让其中一项覆盖另一项;每次都分配全新的值,又可能让本来可以合并的更新继续堆积。是否使用替换功能,并不是唯一的设计选择,替换组应当有多大同样重要。
这里也不能把 Topic 理解成通用消息总线上的全局频道。它用于同一订阅内待发消息的关联,不自动提供跨订阅广播、业务对象识别或版本排序。一个名称取得再规范,也不会替团队完成这些工作。
替换的并不只是那段文字
新通知带来的变化还包括投递条件。RFC 的替换操作会更新原消息保存的 TTL、Urgency,以及与之关联的回执订阅。只比较新旧正文,可能看不见保留期限缩短或紧急程度降低。
例如,两套发送程序可能为同一个业务对象生成更新,却采用不同的默认期限。后到的一条内容也许只增加了少量信息,同时把等待时间改得更短。用户稍晚恢复连接时,能否收到有用内容,便不再只是正文是否正确的问题。
紧急程度也需要联系设备的接收条件理解。接收方可以表达自己愿意接收的最低紧急等级。如果替代消息降低了等级,待发内容的投递资格可能随之改变。这不一定是错误:事情可能确实已经不再紧急。问题在于,这应当是一项有解释的策略,而不只是两个组件默认值碰巧不同。
保留期限则有明确的资源边界。发送方请求的时长并不是无限存储权利,服务可以采用更短期限,也可能因运行约束提前终止保存。把 TTL 调大,不能代替对设备返回时间、业务有效期和恢复来源的判断。
由此,产品变更审查应当把内容与投递参数当作同一份策略来读。谁改变了正文,谁改变了紧急程度,谁改变了等待时间,不一定是同一个组件;但对接收者来说,这些变化共同决定最后留下什么。
业务上的“更新”可能比到达顺序更复杂
在单一发送程序中,连续覆盖很容易看起来像“永远保留最新状态”。当多个生产者参与、请求发生延迟时,“最新”就出现了两种含义:业务版本较新,或推送服务较晚接受。
一个携带旧快照的请求如果晚到,仅靠 Topic 相等这一条件,服务并不知道它在业务上更旧。尤其在内容已经加密的情况下,不应假定中间服务会替应用比较版本、合并字段或识别一项操作是否已经失效。
应用可以协调多个生产者的发送顺序,也可以让接收端辨识不应接受的旧版本,或者要求接收端重新读取权威状态。这些是可选择的设计方向,不是本文替 IETF 增加的强制要求。应当选择哪一种,取决于任务是否只需要当前状态,还是必须保留每次变化以及变化造成的后果。
在途旧消息的问题也应放进同一判断。一个替代通知可能最终到达,却不能据此推定旧通知没有先被处理。某个旧回执没有出现,也不能反过来证明没有人看过旧内容。队列、传输、应用处理和人的行为不是一个能够共同回滚的事务。
当消息仅用于提醒“请打开应用查看当前任务”,这种时序可能比较容易吸收。当消息直接驱动持久动作时,成本和恢复要求就不同。重要的不是给某类消息统一贴上“安全”标签,而是把承诺限定在确实能控制的范围。
屏幕上的合并是另一道工序
用户只看到一条提醒,也不意味着推送服务只投递过一条消息。WHATWG Notifications 标准 对展示层另有替换规则:在同源范围内,具有相同非空 tag 的通知可以按规定流程替换,具体处理还考虑平台是否支持原生替换。
这里的 tag 与推送服务处理的 Topic 不是一个自动贯通的字段。应用可以主动协调两者,但不能假定服务端队列的关联值会原样传给终端,替自己确定屏幕上的分组方式。
这一点对产品统计很有影响。一个干净的通知栏可能是展示层主动合并的结果,背后仍有多个任务等待处理。相反,多个设备也可能各自显示与同一工作项有关的提醒。衡量是否存在重复工作或遗漏工作,需要回到应用状态,而不只是数屏幕上的卡片。
W3C 于 2025 年 12 月 1 日发布的 Push API 工作草案 进一步区分了消息接收后的处理路径,包含普通 service worker 处理和声明式通知机制。其算法在某些失败情况下也会确认消息,以避免持续重复投递,例如无法解密或反复处理失败的情形。
因此,投递确认不能直接变成“任务完成”这一业务事实。这里引用的是明确日期的工作草案,而非所有浏览器的实现证明。本文没有进行设备测试,也没有建立某项功能的实际普及率;这些限制不妨碍我们把接收、解释、展示与执行分开观察。
加密保护内容,不替应用定义信息取舍
消息加密规范 RFC 8291 为 Web Push 内容提供保护,同时明确指出,该内容加密机制不保护 HTTP 头字段。接收端应当把这些字段视为来自推送服务的信息,而不是直接来自应用的端到端承诺。
这不是说传输没有保护。TLS 仍然保护与外部观察者之间的通信。需要区别的是:消息内容受到的保护,并不能自动延伸为中间服务不可影响的排序、替换或业务解释规则。
应用正确理解更新所需的对象、版本等信息,应当有适当的内容或原始数据依据,而不是借用一个投递头字段的名字来保证其真实性。具体采用怎样的格式和校验,仍属于应用设计,本文不声称存在统一的标准答案。
关联值也不适合为了方便调试而随意暴露敏感业务含义。加密正文并不会让中间服务看不到消息之间的关联。可诊断性与信息暴露需要一起权衡,不能把“保留所有调试信息”当作没有代价的默认选项。
最终,安静的通知通道是否更好,取决于它省下了什么。删掉已经失去用途的中间提醒,可能改善产品;删掉某件重要事情唯一剩下的记录,则可能让恢复成为不可能。推送服务可以执行有限的资源管理,业务方必须为两者之间的差别负责。
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
