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

Posted

技术标签:

【中文标题】HTTP Server-Push:服务到服务,无需浏览器【英文标题】:HTTP Server-Push: Service to Service, without Browser 【发布时间】:2021-02-10 01:55:52 【问题描述】:

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

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

我的大部分服务 API 都类似于 REST:它接收 HTTP 请求、处理它们,然后发回 HTTP 响应。

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

    由于业务的性质,客户端系统无法承受打开自己的 HTTP API 端点,因此我的服务无法与它们建立出站 HTTP 连接以传递数据状态通知。 IE。不能使用 WebHooks。

    同时,我的服务利益相关者需要记录确认数据状态通知已被客户端系统接受,因此像 Amazon SNS 这样的即发即弃系统似乎并不适用。

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

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

相关问题和资源

REST API with active push notifications from server to client Is ReST over websockets possible? Can we use Web-Sockets for Communication between Microservices? What is difference between grpc and websocket? Which one is more suitable for bidirectional streaming connection? https://www.smashingmagazine.com/2018/02/sse-websockets-data-flow-http2/

【问题讨论】:

我的第一直觉是:websockets 【参考方案1】:

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

网络钩子

这是客户端自己打开端点的时候。只要服务有一些通知要传递,服务就会调用客户端的端点。这样,客户端也充当服务,因此客户端和服务在通知传递期间交换角色。

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

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

另一个问题是流量控制:应在服务中采取特殊措施,以免大量连接、请求和/或数据使客户端不堪重负。

轮询

在这种情况下,客户端仍然是客户端,服务仍然是服务,这与 WebHooks 不同。该服务提供了一个端点,客户端可以在其中不断请求新通知。此选项的优点是它不会改变连接方向和请求-响应方向,因此它适用于基于 HTTP 的服务。

需要注意的是,轮询 API 应该具有一些丰富的语义,以便在如果通知丢失是不可接受的情况下相当可靠。很好的例子可能是 Google Pub/Sub pull 和 Amazon SQS。

这里有几个注意事项:

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

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

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

    如果服务没有要传递的通知,它应该通过不立即传递空响应来阻止客户端的调用一段时间。否则,如果客户端循环轮询并立即响应,则循环迭代将很短,客户端将向服务提出过多请求,从而增加网络、解析负载和请求计数。一个不错的功能是,一旦出现一些交付通知,服务就会取消阻止并响应客户端。这有时被称为“长轮询”。

HTTP 服务器发送的事件

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

缺点是通信是单向的,如果成功处理了事件,客户端无法通知服务。由于没有这种反馈,服务可能难以控制事件的发生率以防止让客户端不堪重负。

WebSockets

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

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

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 端点),并让客户端直接从队列接收通知。

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

常见注意事项

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

从服务端监控客户端处理错误是很有价值的。通过这种方式,服务所有者无需询问即可知道哪些客户端更糟糕。

如果使用队列(隐式或显式),则监控队列的长度和最旧通知的年龄是很有价值的。它让服务所有者可以判断客户端中数据的陈旧程度。

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

【讨论】:

【参考方案2】:

解决服务器→客户端通知问题的 WebSockets 的一种替代方法似乎是gRPC。

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

缺点:

与 HTTP 相比,支持的语言和平台并不多。如果基于 HTTP 1.1,问题中的替代 C 将更易于访问。 WebSockets 的使用时间也更长,我预计会比 gRPC 更广泛地采用。 目前似乎并非所有 gRPC 实现都支持 XML 格式的数据 according to FAQ。为了传输 XML,我的服务及其客户端必须将 XML 消息作为 gRPC protobuf 消息中的字节数组传输。 使用 gRPC,TLS 终止 cannot be done 在通用 HTTP 1.1 负载平衡器上。需要一个应用层 HTTP/2 感知反向代理(负载均衡器),例如 Traefik。 有像 this 和 this 这样的方法允许 HTTP 1.1 兼容协议,但它们有自己的限制,例如可用客户端数量有限或必要的客户端自定义。

【讨论】:

以上是关于HTTP Server-Push:服务到服务,无需浏览器的主要内容,如果未能解决你的问题,请参考以下文章

Jetty HTTP2 服务器推送支持

Discord 上的 HTTP 请求和响应,无需邀请官方 discord 机器人到该服务器

使Java HTTP会话无效以重定向到其他服务器,而无需将用户注销

无需上传附件到服务器,Servlet读取Excel

JavaCV音视频开发宝典:无需流媒体服务也无需转码,使用JavaCV和springBoot实现http-flv转封装直播服务,浏览器网页flv.js直接播放rtprtsprtmp实时视频

JavaCV音视频开发宝典:无需流媒体服务也无需转码,使用JavaCV和springBoot实现http-flv转封装直播服务,浏览器网页flv.js直接播放rtprtsprtmp实时视频