Server-Sent Events 单向推送
Server-Sent Events 在一条持续的 HTTP 响应中传递服务端到客户端的更新。它不把 HTTP 变成双向套接字,自动重连也不会让投递自动具备持久性。事件分帧、连接寿命、历史保留和业务确认,是四个需要分别定义的契约,不能因为都发生在一条流里,就用一个“推送成功”概括。 一份响应包含有边界的文本事件 SSE 响应使用 text/event-stream 类型和 UTF-8 编码。字段按行组织,空行结束一个块;同一事件中的多行 data 会用换行符拼接。以冒号开头的注释行不作为事件数据处理,因此可以承载心跳,而不触发一次业务消息。缺少正确的空行边界,字节到达也可能暂时看不到事件。 event 字段指定事件类型;没有该字段时,默认类型是 message。命名为 update 的事件需要注册 update 监听器,不能只依赖 onmessage。流在事件尚未结束时到达 EOF,也不会仅仅因为连接关闭就派发这份不完整事件。因此,网络层确实收到字节,不等于事件分帧和监听器选择已经正确。 id 字段是字符串,不是协议强制要求单调递增的整数。应用可以选择序号,但排序方式与保留策略仍要自己定义。空 id 会重置保留的编号,而不是确认此前所有业务已经完成。不要把一个用于续传定位的字段,直接解释成数据库提交记录或可靠消息队列的消费确认。 重连仍然依赖历史记录 浏览器重连时可以通过 Last-Event-ID 发送保留的事件编号。这个值是游标提示,不是事务确认。服务端需要一份经过权限约束、仍然保留的历史,以及“从游标之后继续”的明确规则。重新建立连接,无法恢复从未记录的事件,也无法凭空找回已经过期删除的内容。 应定义游标失效后的行为,例如重新获取快照、拒绝续传,或遵循其他明确业务约定。悄悄从最新事件继续,会隐藏中间的缺口。消费者可能还需要去重;收到一个事件,并不能证明后续动作已经恰好执行一次,也不能保证重试后不会再次触发相同副作用。 EventSource 会对可恢复的断连执行重连,HTTP 204 则明确要求停止重连;其他终止条件也存在,并非每种错误都会无限重试。retry 字段表达以毫秒计的重连间隔,但重连调度不是业务投递截止时间,不能拿它充当消息在多少毫秒内必达的承诺。 坏掉的样子 处理函数或中间代理可能缓冲输出,让应用已经生成的事件迟迟不到客户端。源站需要刷新输出,还要沿真实代理路径观察交付。心跳能够帮助维持某些空闲计时边界,却不能推翻固定的请求总期限。订阅队列也要有界,并规定消费者跟不上时是断开、丢弃还是要求重新同步。 认证和 CORS 规则仍然适用。原生 EventSource 提供凭据模式,而不是任意请求头配置,不能假设它接受与 fetch 完全相同的认证参数。多个页面同时订阅时,还要考虑 HTTP/1.x 连接上限与 HTTP/2 流上限;多路复用不会取消连接级资源预算,也不会替服务端解决慢消费者问题。 ...