HTTP Server-Push:服务到服务,无需浏览器

问题描述

我正在开发基于云的后端HTTP服务,该服务将公开与某些本地系统集成。客户端系统是由外部供应商定制的,它们是具有自己数据库的后端系统。这些系统已部署在我们客户的公司中,我们无法访问它们,也无法控制它们。我们正在向供应商提供我们的API规范,并且他们实现了客户端代码

我的服务与客户端交换的数据格式基于XML,并遵循一定的标准。供应商以不同的编程语言实现其客户端系统,并且随着时间的推移会出现新的供应商。我希望有尽可能多的客户能够使用我的服务。

我的大多数服务API都类似于REST:它接收HTTP请求,对其进行处理并向后发送HTTP响应。

此外,我的服务会累积一些数据状态更改,并且需要定期将此数据推送到客户端系统。由于以下限制,该用例似乎不适合传统的客户端-服务器HTTP请求-响应模型。

  1. 由于业务的性质,客户端系统无力打开自己的HTTP API端点,因此我的服务无法与其建立出站HTTP连接来传递数据状态通知。即不能使用WebHooks。

  2. 与此同时,我的服务涉众需要记录确认信息,即客户端系统接受了数据状态通知,因此,像Amazon SNS这样的“一劳永逸”系统似乎不适用。

我当时正在考虑解决该问题的几种方法,但是不确定是否缺少一些简单的选项或某些已经解决了该问题的技术。因此,这个问题。

问题文本已更新:选项已移至我自己的答案。

相关问题和资源

解决方法

我最终在自己的团队的帮助下找到了自己的问题的答案。对于像我这样的人,这里的问题是“我如何安排从服务到其客户的通知传递”,这里是可用选项的概述。

WebHooks

这是客户端打开端点iself的时间。每当服务有一些要传递的通知时,该服务就会调用客户端的端点。这样,客户端还可以充当服务,因此客户端和服务在通知传递过程中会互换角色。

使用WebHooks,客户端必须能够使用已知地址打开终结点。如果客户端的软件在NAT或防火墙后面运行,或者客户端是浏览器或移动应用程序,则这很复杂。

需要准备该服务,以使客户端的WebHook端点可能并不总是在线并且可能并不总是健康的。

另一个问题是流量控制:服务中应采取特殊措施,以使大量连接,请求和/或数据不至于使客户不知所措。

投票

在这种情况下,客户端仍然是客户端,而服务仍然是服务,这与WebHooks不同。该服务提供了一个端点,客户端可以在该端点上连续请求新的通知。此选项的优点是它不会更改连接方向和请求-响应方向,因此可以与基于HTTP的服务一起很好地工作。

警告是,如果无法接受通知丢失,则轮询API应该具有一些丰富的语义,以便合理可靠地。很好的例子是Google Pub / Sub pull和Amazon SQS。

以下是一些注意事项:

  1. 接收和删除通知应该是单独的操作。否则,如果服务在将通知发送给客户端之前删除了通知,并且客户端无法处理该通知,则该通知将永远丢失。当删除操作与接收操作分开时,客户端将被迫明确执行删除操作,这通常在成功处理之后发生。

  2. 如果客户端收到通知但尚未删除它,则可能不希望让其他参与者处理同一通知(也许是同一客户端的并发处理)。因此,在第一次收到通知后,必须将其隐藏起来。

  3. 如果客户端由于错误,网络丢失或进程崩溃而未能在合理的时间内删除通知,则该服务必须使通知可见以便再次接收。这是重试机制,可以使通知最终得到处理。

  4. 如果服务没有要发送的通知,则应该在一段时间内不立即发送空响应来阻止客户端的呼叫。否则,如果客户端在循环中轮询并且响应立即到来,则循环迭代将很短,并且客户端将向服务不断增加的网络提出过多请求,从而分析负载和请求计数。一项不错的功能是,一旦出现要发送的通知,服务便会解除阻止并响应客户端。有时称为“长时间轮询”。

HTTP服务器发送的事件

使用HTTP服务器发送的事件,客户端打开HTTP连接并向服务发送请求,然后服务可以发送多个事件(通知),而不是单个响应。连接是持久的,服务可以在事件准备就绪后立即发送。

缺点是通信是单向的,客户端无法通知服务是否成功处理了事件。由于缺少此反馈,因此服务可能难以控制事件的发生率以防止客户不知所措。

WebSockets

创建

WebSockets是为了启用任意双向通信,因此这是该服务向客户端发送通知的可行选项。客户还可以将处理确认发送回服务。

WebSockets已经存在了一段时间,并且应该得到许多框架和语言的支持。 WebSocket连接始于HTTP 1.1连接,因此许多负载均衡器和反向代理都应支持基于HTTPS的WebSockets。

WebSockets经常与浏览器和移动客户端一起使用,很少用于服务到服务的通信。

gRPC

gRPC在某种程度上类似于WebSockets,它启用任意的双向通信。 gRPC的优点是它围绕协议和消息格式定义文件为中心。这些文件用于生成代码,这对于客户端和服务开发人员至关重要。

gRPC用于服务到服务的通信,并且具有grpc-web的浏览器客户端也支持它。

gRPC在多种流行的编程语言和平台上受支持,但其支持范围比HTTP窄。

gRPC在HTTP / 2上运行,这可能会导致反向代理和负载平衡器(如TLS终止)出现问题。

消息队列(PubSub)

最后,服务和客户端可以使用消息队列作为通知的传递机制。该服务将通知放在队列上,客户端从队列中接收通知。队列可以由RabbitMQ,Kafka,Celery,Google PubSub,Amazon SQS等许多系统之一提供。具有多种不同属性的排队系统可供选择,选择一个队列本身就是一个挑战。例如,也可以使用数据库来模拟队列。

必须在服务与拥有队列的客户(即为该队列支付的费用)之间确定。无论哪种方式,只要服务需要向其推送通知,排队系统和队列就应该可用,否则通知将会丢失(除非服务在内部使用另一个队列缓冲它们)。

队列通常用于服务到服务的通信,但是某些技术还允许浏览器作为客户端。

值得注意的是,上面列出的其他选项中的服务端可能会使用“隐式”内部队列。原因之一是为了防止在没有客户端可以接收通知时丢失通知。还有许多其他很好的理由,例如让客户端按照自己的节奏处理通知,允许最大处理吞吐量,允许以固定容量处理尖峰流量。

在此选项中,队列被“显式”用作传递机制,即该服务不在队列前面放置任何其他机制(HTTP,gRPC或WebSocket端点),并允许客户端直接从队列接收通知。

消息传递在组织微服务通信中很流行。

常见注意事项

在所有选项中,必须确定服务,客户和企业的通知丢失是否可以容忍。如果由于处理错误,不可用等原因可以丢失通知,则可以进行一些更简单的技术选择。

从服务端监视客户端处理错误非常有价值。通过这种方式,服务所有者无需询问客户就可以了解哪些客户受到了更多的破坏。

如果使用了队列(隐式或显式),则监视队列的长度和最早的通知的时间很有价值。它使服务所有者可以判断客户端中的过期数据。

如果通知传递的组织方式是仅在客户端成功处理通知之后才删除通知,当客户端无法处理该通知时,同一通知可能会陷入无限接收循环中。这种通知有时被称为“毒药消息”。服务或排队系统应删除毒药消息,以防止客户端陷入无限循环。一种常见的做法是将有毒信息移到一个特殊的位置,有时称为“死信队列”,以供以后的人工干预。

,

对于WebSockets来说,用于服务器→客户端通知并带有客户端确认的问题的一种替代方法似乎是gRPC

  • 它以bidirectional streaming模式支持服务器与客户端之间的双向通信。
  • 它可在HTTP 2.0之上运行。对于我们来说,通过HTTP端口运行是必不可少的。
  • 有用于多个流行languages and platforms的客户端和服务器生成器。一件好事是,我可以与供应商共享协议定义文件,并可以确保我的服务和他们的客户使用相同的语言。

缺点:

  • 与HTTP相比,不支持许多语言和平台。如果基于HTTP 1.1,则可以更方便地访问问题的替代C。 WebSocket的使用时间也更长了,我希望它能比gRPC得到更广泛的应用。
  • 并不是所有的gRPC实现目前似乎都支持数据according to FAQ的XML格式。为了传输XML,我的服务及其客户端必须将XML消息作为字节数组传输到gRPC protobuf消息内部。
  • 使用gRPC,通用HTTP 1.1负载平衡器上的TLS终止cannot be done。需要应用程序层支持HTTP / 2的反向代理(负载均衡器),例如Traefik。
    thisthis之类的方法允许HTTP 1.1兼容协议,但是它们有其​​自身的限制,例如可用客户端数量有限或必需的客户端自定义。