事件(event)是「从一个状态到另一个状态的显著变化」,比如收件箱从「无新邮件」变成「有一封新邮件」。这个变化可以被内部反应(邮件程序发现自己收到新信)、外部反应(用户看到通知),或用来触发另一个事件(未读计数 +1)。
事件驱动架构之所以受 API 开发者青睐,是因为它在异步环境里表现出色:API 只需在新事件到达时触发某函数,不必苦等同步交付。去掉「不断轮询端点」的浪费,既省硬件又降单次调用开销,比轮询类方案在算力、带宽、协同处理上都更优。
客户端-服务器、服务器-服务器之间的关系,是 API 设计里最常被讨论的命题。事件驱动架构让系统对「状态变化」做出反应,免去不断轮询。本文编译自 Nordic APIs(2017-06-02,作者 Kristopher Sandoval),拆解 5 种主流事件驱动协议:WebSockets、WebHooks、REST Hooks、Pub-Sub、Server Sent Events,并给出各自优缺点。
事件(event)是「从一个状态到另一个状态的显著变化」,比如收件箱从「无新邮件」变成「有一封新邮件」。这个变化可以被内部反应(邮件程序发现自己收到新信)、外部反应(用户看到通知),或用来触发另一个事件(未读计数 +1)。
事件驱动架构之所以受 API 开发者青睐,是因为它在异步环境里表现出色:API 只需在新事件到达时触发某函数,不必苦等同步交付。去掉「不断轮询端点」的浪费,既省硬件又降单次调用开销,比轮询类方案在算力、带宽、协同处理上都更优。
WebSocket 在多数浏览器里是「内建」的,它提供单条 TCP 连接上的全双工通信,由 IETF 标准化为 RFC 6455。基于 TCP,附带一个 HTTP「Upgrade」握手以互通。
优点:专为浏览器设计,开销极低;全双工让两端同时收发,吞吐好;走 80/443 端口,传统因安全屏蔽非 Web 应用的环境也能通;被所有主流浏览器原生支持。缺点:它不是 HTTP,缓存、代理等 HTTP 优化用不上;长连接「常驻」意味着几乎无法水平扩展——要扩容只能加端口匹配最大负载。
WebHook 类似 WebSocket,但核心是自定义回调(把一段代码当参数传进去,在指定时刻执行),本质是一套「if this, then do」机制,让事件触发方之外的人在自己的系统里定制响应。2007 年由 Jeff Lindsay 提出,典型如 GitHub 推送代码后触发 CI 构建。
优点:与 WebSocket 不同,WebHook 在 server-to-server 场景表现好;并且它基于 HTTP,无需新基础设施,接入快、搭建简单。缺点:很多能力其实 REST 也能做,事件驱动若能被 REST 镜像,卖点就弱;WebHook 对客户端和服务器都可能很耗资源——一端要通知很多服务器、另一端要监听很多客户端时,网络会失控膨胀。
REST Hooks 相当于「把 Hook 烘焙进 REST 本身」,由 Zapier 提出:把 hook 收拢到一个目标 URL 作为订阅,资源变化时 ping 订阅方。它是对轮询的回应——客户端不再反复查,而是等变化并响应。简单说,就是「REST 里的 WebHook」。
优点:被动接收而非持续轮询,省下大量客户端算力;而且是订阅式,订阅即用,非常简单直观。缺点:它其实违背了 REST「无会话、无状态」的本意——本质上把轮询从一端移到了另一端;也有人认为 TCP 早已解决了它想做的事,再在 HTTP 上叠一层是多此一举。
Pub-Sub(publish-subscribe):事件发布到一个「类」上,发布者无需知道谁在订阅;用户只声明自己加入哪些类、关心哪些事件,事件推送来就收。常被比作电台:唱片公司发音频给电台,电台广播给听众,双方互不知道对方是谁——这种「分割」是模式自带的。Google Cloud Pub/Sub 就是该理念的一种云实现。
优点:松耦合,因此极度可扩展、灵活;天然适合测试——订阅者只收某类事件,出问题时分割能直接定位故障类与受影响用户。缺点:解耦也是硬伤——作为中间件,它无法有效告知发布者「消息已送达」,监听方与事件分离,可能根本不知道某条本该发的消息没发成;高流量下子类层层套娃会带来不稳定与复杂度。
SSE 与 WebSocket 类似,但是单向数据:服务器持续自动向客户端推送更新,由 W3C 在 HTML5 下标准化,兼容任何支持 HTML5 的方案。
优点:单向意味着带宽更低,连接可以是临时的而非常驻;只需关心一个方向的数据流,无需定义消息交换协议、无需等待双向回报,复杂度大幅降低。缺点:正因单向,它不适合需要双向通信的场景;安全与认证靠 header 转发,而 JavaScript 的 EventSource 原生不支持改 header,会给接入者带来麻烦;且只能在「整包发完失败」后才发现客户端断开,失败连接多了数据丢失会快速累积。
没有万能协议,按需求取:① 浏览器内双向实时(聊天、直播计算)→ WebSocket;② server-to-server 回调、轻量异步通知 → WebHook;③ 想要最简单订阅式推送 → REST Hooks;④ 需要松耦合、高扩展、多对多 → Pub-Sub(Kafka/Pub/Sub);⑤ 只需服务端单向推送、省带宽 → SSE。多数平台会组合多种协议,按「是否双向、是否 server-to-server、是否需严格送达」三问快速定位。