事件驱动API架构的五个协议


在这篇文章中,我们将讨论 5 种常见的事件驱动方法——WebSockets、WebHooks、REST Hooks、Pub-Sub和Server Sent Events。我们将定义它们的本质和作用,以及 API 提供者如何使用它们。此外,我们将针对每种方法提供一些优缺点,以便为您的平台轻松直观地选择解决方案。

什么是事件驱动架构?
事件驱动的架构建立了一个可以被消费和反应的事件。但什么是事件?

事件本质上是指从一个状态到另一个状态的任何重大变化,例如从收件箱中没有邮件到收件箱中有新邮件的变化。这种状态可以被内部反应(例如当有关的电子邮件程序意识到有新邮件被收到时),外部反应(当用户看到新邮件的通知时),或者用来产生另一个事件(例如,消息计数增加了一条)。

事件驱动架构对API开发者很有吸引力,因为它们在异步环境中功能非常好。通过精心设计API,在新的事件交付时触发某些功能,API系统不需要内在地等待同步交付或实时通信。这是非常有益的,因为消除不断轮询端点的需要,可以从其他浪费的用途中释放资源,减少一般的硬件要求和特定的调用开销。

由于这个原因,事件驱动架构非常非常受欢迎,与其他解决方案和架构(如轮询和其他以轮询为中心的衍生物)相比,事件驱动架构导致了功率、带宽和协同处理的改善。

1: WebSockets
WebSockets是一种有趣的事件驱动解决方案,因为对于大多数浏览器来说,它们实际上已经被植入了应用程序本身。本质上,WebSocket是一个在单个TCP连接上提供全双工通信的协议。它由互联网工程任务组作为RFC 6455进行标准化,Web IDL中的WebSocket API后来也在W3C的旗帜下进行了标准化。

虽然该协议本身是为了在网络浏览器和服务器之间使用,但该协议可用于存在客户-服务器关系的任何情况。该协议本身以TCP为基础,增加了被视为 "升级请求 "的HTTP解释语句,以实现互操作性。

优点:
由于WebSocket是专门为浏览器操作而设计的,因此就其实际作用而言,它的开销极低。通过使用标准化的方法建立全双工对话,两个实体之间的连接可以同时进行,从而降低开销,提高吞吐量。

此外,这些通信是通过TCP 80/443进行的,这意味着传统上出于安全原因阻止非网络应用的环境仍然可以处理这个协议,因为防火墙允许从这个端口进行通信。

也许使用WebSockets的最有力的论据是,它们是标准化的,所有主要的浏览器都支持,从Microsft Edge到Opera,从Firefox到Chrome。这意味着,任何与之相连的网络应用都可以在绝大多数基于浏览器和独立于浏览器的网关和应用中进行交互。

缺点:
WebSockets有一个明显的主要缺陷--虽然它可能支持类似HTTP的功能,但它完全不是HTTP。这有一定的影响,特别是在考虑优化HTTP时,如缓存、代理等,这一点还不太明显。


2:WebHooks
WebHooks是一个类似于WebSocket的概念。它们的主要功能是使用自定义回调,或将代码作为参数传递给另一块代码,并在指定时间点执行。从本质上讲,WebHook是一个美化的 "如果这样做,那么就这样做 "的系统,允许独立于事件发射的用户在自己的系统中对该事件做出自定义响应。

这个术语是由Jeff Lindsay在2007年创造的,并迅速在那些希望对外部行为创建自动响应的用户中流行起来。一个很好的例子是,一个开发者向GitHub推送了一个新项目,这引起了一个事件。用户有一个与WebHook的URI绑定的系统。当推送被发布后,用户的系统利用WebHook的URI将推送整合到一个更大的构建中,从而创建一个编译的组件。

优点:
WebHooks的功能很像WebSockets,但它们在一些关键领域是不同的。首先,WebSockets主要是为基于浏览器的通信而设计的,虽然它们可以在任何客户端-服务器通信中使用,但它们在服务器到服务器的设置中表现得并不好。

另一方面,WebHooks由于其操作方式,在服务器到服务器的系统中工作得非常好。因为该系统本质上的功能是前面提到的 "如果这样做",服务器可以被配置为在任何时候与预先形成的URI相联系,并在该事件被触发时执行一个给定的功能。

此外,WebHooks有一个独特的好处,那就是基于HTTP,与WebSockets不同。这意味着该系统可以在不使用任何新的基础设施的情况下进行整合,允许快速采用和相对简单的设置。

缺点:
WebHooks的问题是,它们的很多功能已经可以放在可以说是更强大的REST架构方法上。虽然采用事件驱动的架构通常是正在建立的服务的要求,但当它可以反映在REST中,同时也提供REST给用户的丰富选项时,这是一个难点。


不过,这些RESTful解决方案,如RestMS,本质上只是简单的消息查询服务,确实需要额外的基础设施,考虑到应用的目的,这可能是可行的,也可能不是。

此外,WebHooks对客户端和服务器都是资源密集型的。如果客户端需要通知许多服务器一个事件的发生,而服务器需要监听大量的客户端通知这一变化,那么你很快就会遇到你的网络不受控制地增长的情况。虽然HTTP的扩展性相当好,但这是一个绝对需要考虑的负面因素。

然而,也有一些方法可以在HTTP之上建立一个消息队列服务--一些RESTful的例子包括IronMQ和RestMS。

由于WebSockets相对较新,在2011年才被正式标准化,业界仍在理解其副作用的含义。大多数使用WebSockets的应用都是专门为WebSocket的一切而设计的--然而,有待观察的是,从长远来看,这种解决方案是否比目前的任何无状态解决方案更好。

当然,还有一个事实是,与本列表中的其他架构一样,WebSocket在数据传输过程中创建了一个 "始终在线 "的连接。虽然这对许多用途来说是好的,如媒体流和实时流计算,但它也基本上意味着,对于WebSockets来说,没有可扩展性。端口有硬编码的限制和带宽,因此为了 "扩展",你必须增加额外的端口来匹配最大的负载。在无状态系统中,这不是一个问题,因为请求可以以独立于服务器本身状态的方式等待和进行。

3: REST Hooks
说到RESTful的例子,REST Hooks本质上是将 "钩子 "烘烤到REST本身。这被定义为来自Zapier的一项倡议,这是我们以前讨论过的主题--钩子被整理成一个单一的目标URL作为订阅,当注意到一个变化时,它就会向资源请求者发出请求。

这种方法是对轮询做法的回应,即客户端不断检查资源的变化。在REST Hooks范式下,客户端会等待变化,并对其做出反应。简单地说,这就是REST中的WebHook。

优点:
在正确的情况下,REST Hooks显然是非常强大的--能够被动地接收一个资源,而不是把处理能力用于不断的轮询,这释放了大量的客户端成本。

也许REST Hooks最有力的论据是,它的使用非常简单和直观。虽然WebHooks利用了HTTP,因此不需要新的架构来设置,但它们也受到了限制,因为它们是建立在HTTP之上的,因此要正确设置和有效使用可能会有些复杂。

不过,REST Hooks是基于订阅的,因此,只要订阅就可以使用。这使得它成为一个非常容易使用的解决方案,同时提供了很多更复杂系统的可用性和有效性。

缺点:
当然,每个解决方案都有其缺点,REST Hooks也不例外。可以认为,REST Hooks实际上违背了REST的本质--无会话和无状态。REST Hooks本质上创造了一致的轮询,它只是把轮询从一边转移到另一边。

然后,还有一个有争议的问题,就是REST Hooks可能在做一些已经被解决的事情。有些人认为,TCP已经做了大部分REST Hooks想要做的事情,简单地在HTTP上分层来获得TCP已经做的事情是一种糟糕的方法。

4:Pub-Sub
Pub-Sub是一种稍微不同的方法。它的全称是 "发布-订阅",其概念是:事件被发布到一个类中,而订阅该类的客户端并不知情。基本上,一个用户将加入一个或多个类,然后在不考虑或不知道事件发布者的情况下接收事件更新。

这里的主要区别是对提供者的有意识选择--在本文提到的其他解决方案中,用户有意识地与一个给定的服务器或提供者进行通信,并按照预先确定的方式接收事件。在Pub-Sub方案下,用户只需指定他们希望成为哪个类别的成员,以及他们对接收哪些事件感兴趣。从那里,当一个事件被推送出去时,他们就会收到这些事件。

在互联网的讨论中,这种方式通常是以广播频道为框架的。唱片公司,或出版商,向电台发布音频,然后电台向听众,或订阅者广播这些音频。在这里,Pub-sub是电台的中间人--听众不知道谁给了电台音乐,公司也不知道听众是谁。正是这种细分的模式被烘托出来了。

当我们谈论Pub-Sub时,我们需要记住,我们实际上是在谈论两个不同的东西。Pub-Sub可以指编程方面的方法和一般概念,但它也可以指基于该方法的具体供应商解决方案。例如,谷歌的云Pub/Sub是他们云服务中一般方法的实现,并允许上述的异步多对多的Pub-Sub关系。

优点:
Pub-Sub的一个巨大好处是它是松散耦合的,因此具有极大的可扩展性和灵活性。事件提供者唯一要做的就是生成内容--其他每个步骤都是通过一个独立的中间人完成的,因此内容很容易被扩展和调节到解决方案的架构和设计中。

此外,Pub-Sub很适合测试。用户被严格限制在他们所要求的一类事件中,所以如果发生故障,这种自然的分割会告知供应商故障在哪里,以及哪一类用户遇到了故障。

缺点:
不幸的是,解耦也是这种模式的一个巨大缺点。作为一个中间人,Pub-Sub不能有效地通知提供者一个消息已经被发送,而且听众与事件分离,因此可能不知道是否有一个应该被发送的消息没有被发送。回到广播的解释,听众永远不会知道一首歌是否应该在某个频道上播放,或者该频道是否超出了范围,一旦唱片公司的管理人员把音乐交出去,他们就不知道用户是否收到了歌曲而没有直接的反馈。

此外,虽然该系统具有可扩展性和灵活性,但在高流量负荷下确实会出现不稳定的情况,因为可能会构建一个又一个子类来处理进一步的细分。这当然会导致前面提到的不稳定,此外,复杂性也会增加。

你必须记住,虽然这个模型中发布者和订阅者之间的关系可能是有益的,但当这些关系需要被调节时,它也会带来自己的困难。虽然你当然可以绕过这个问题,但在这一点上,你是在与模式的基础作斗争,而不是与任何次要的性质作斗争--你在试图制造脱水的水,而与模式的性质作斗争表明该模式本质上是差的。

5: 服务器发送事件
服务器发送事件,或称SSE,是一种很像WebSockets的通信协议,但有单向数据的含义。在这种架构中,服务器作为一个自动过程持续向客户端发送更新。这是W3C在HTML5下制定的标准,因此与任何同样与HTML5兼容的解决方案都是兼容的。

值得注意的是,有一个来自网络超文本应用技术工作组的竞争性标准化--这是脱离 "HTML5 "而进入WHATWG所称的 "HTML生活标准 "运动的遗留物。一般的工作共识是,WHATWG的标准化在罕见的标准分歧的情况下会被优先考虑。随着时间的推移,这可能会成为一个更多的问题,因为WHATWG的创建是由于人们认为W3C对HTML的发展缺乏兴趣,但就目前而言,两种标准都是可以接受的。
虽然理论上很简单,但考虑到好处和坏处,服务器发送的事件并不简单。

优点:
SSE在其通信中不是双向的--服务器以一种稳定的、可预测的方法发布事件。这对那些不需要WebSockets或其他此类解决方案中的双向通信的应用程序非常有利,因为这意味着更低的带宽,并允许连接是暂时的,而不是在数据传输过程中始终在线。就其性质而言,数据是单向传输的,因此不需要等待数据的返回。

此外,至少在理论上,SSE在复杂情况下更容易设置。你只需要担心数据通过一个系统向一个方向传输,从而极大地降低了复杂性。不需要定义消息交换协议,不需要指定数据持续时间或等待时间,不需要支持双边消息传递--单一方向节省了大量的复杂性。

缺点:
这种简单性可能是SSE在特定用例中失败的地方。在需要双向通信的情况下,SSE是一个非常糟糕的解决方案,虽然这似乎是显而易见的,但很多开发者会惊讶地发现,有多少系统实际上依赖于双向通信的简单功能。

虽然很多情况下可以通过变通来解决,但开发者选择事件驱动协议的目标应该是找到一个开箱即用的协议,而不是找到一个如果配置得当并有辅助系统可以依赖的解决方案。

还有一个安全和认证的问题。双向系统可以很容易地使用认证方法,而SSE则使用头的转发来处理这个问题。虽然在许多语言和应用程序中可以操作和覆盖头,但JavaScript中的EventSource对象并不支持这一点,这将使许多采用者感到非常头痛。

最后,人们担心过度传输数据会造成效率的损失。双向系统可以确定客户端或服务器何时断开连接,但SSE只有在尝试完整的数据传输并收到注意到的失败后才能确定客户端已经断开连接。正因为如此,数据可能会很快丢失,而且随着许多失败的连接,这种损失会随着时间的推移而急剧增加。

结论
没有一种事件驱动的解决方案可以适用于所有的使用情况。虽然许多人认为事件驱动的解决方案应该是基于REST的,这表明REST Hooks是答案,但其他许多人认为,这完全是因地制宜的,REST并不总是它所吹嘘的银弹。

如果你要在浏览器环境中建立低开销的可扩展性,那么WebSockets是一个很好的解决方案。相反,如果你想获得同样的好处,但在一个非浏览器系统中工作,那么WebHooks应该是你的方法。REST Hooks不仅适用于RESTful服务,而且比两者都更容易设置,因此在低时效、高流量的情况下非常好。如果你需要执行客户端和服务器之间的划分,Pub-Sub可以很好,而这可以进一步用服务器发送的方式建立和控制,甚至更强。

简单地说,最好的解决方案将是适合你的具体情况和构建的解决方案--任何这些解决方案,只要有正确的系统,都是一个很好的解决方案。为此,每个解决方案都有一个非常具体的使用案例。