Mik*_*ike 1 http message-queue server-push websocket grpc
我正在开发一个基于云的后端 HTTP 服务,该服务将公开用于与一些本地系统集成。客户端系统是由外部供应商定制的,它们是具有自己的数据库的后端系统。这些系统部署在我们客户的公司中,我们无权访问它们,也无法控制它们。我们向供应商提供我们的 API 规范,他们实施客户端代码。
我的服务与客户端交换的数据格式基于XML并遵循一定的标准。供应商用不同的编程语言实现他们的客户端系统,随着时间的推移,新的供应商将会出现。我希望尽可能多的客户能够使用我的服务。
我的大部分服务 API 都是类似 REST 的:它接收 HTTP 请求,处理它们,然后发回 HTTP 响应。
此外,我的服务积累了一些数据状态变化,需要定期将这些数据推送到客户端系统。由于以下限制,此用例似乎不适合传统的客户端-服务器 HTTP 请求-响应模型。
由于业务的性质,客户端系统无法打开自己的 HTTP API 端点,因此我的服务无法与它们建立出站 HTTP 连接来传递数据状态通知。即使用 WebHooks 不是一种选择。
同时,我的服务利益相关者需要记录确认数据状态通知已被客户端系统接受,因此像 Amazon SNS 这样的即发即忘系统似乎不适用。
我正在考虑解决这个问题的几种方法,但我不确定我是否错过了一些简单的选项或一些已经解决问题的技术。于是就有了这个问题。
问题文本已更新:选项移至我自己的答案。
最终,我自己并在团队的帮助下找到了问题的答案。对于像我这样带着“如何安排从我的服务向其客户发送通知”的问题来到这里的人,这里概述了可用选项。
这是客户端自行打开端点的时间。每当服务有一些通知要传递时,该服务就会调用客户端的端点。这样,客户端也充当服务,因此客户端和服务在通知传递期间交换角色。
使用 WebHooks,客户端必须能够使用已知地址打开端点。如果客户端的软件在 NAT 或防火墙后面工作,或者客户端是浏览器或移动应用程序,则情况会很复杂。
服务需要做好准备,客户端的 WebHook 端点可能并不总是在线并且可能并不总是健康的。
另一个问题是流量控制:应在服务中采取特殊措施,以免客户端因大量连接、请求和/或数据而不堪重负。
在这种情况下,客户端仍然是客户端,服务仍然是服务,这与 WebHooks 不同。该服务提供了一个端点,客户端可以在其中不断请求新通知。此选项的优点是它不会更改连接方向和请求响应方向,因此它可以很好地与基于 HTTP 的服务配合使用。
需要注意的是,如果通知丢失是不可接受的,那么轮询 API 应该具有一些丰富的语义才能相当可靠。Google Pub/Sub pull 和 Amazon SQS 就是很好的例子。
以下是一些注意事项:
接收和删除通知应该是单独的操作。否则,如果服务在将通知发送给客户端之前将其删除,并且客户端无法处理该通知,则该通知将永远丢失。当删除操作与接收分开时,客户端被迫显式执行删除,这通常在成功处理后发生。
如果客户端收到通知但尚未删除它,则可能不希望让其他参与者处理相同的通知(可能是同一客户端的并发进程)。因此,在第一次收到通知后,必须将其隐藏起来以防止接收。
如果客户端由于错误、网络丢失或进程崩溃而未能在合理的时间内删除通知,则服务必须使通知可见以便再次接收。这是重试机制,允许最终处理通知。
如果服务没有要传递的通知,它应该通过不立即传递空响应来阻止客户端的调用一段时间。否则,如果客户端在循环中轮询并立即响应,则循环迭代将很短,并且客户端将对服务发出过多请求,从而增加网络、解析负载和请求计数。一个很好的功能是,一旦出现一些要传递的通知,该服务就会解除阻止并响应客户端。这有时称为“长轮询”。
通过 HTTP 服务器发送的事件,客户端打开 HTTP 连接并向服务发送请求,然后服务可以发送多个事件(通知)而不是单个响应。连接是长期存在的,服务可以在事件准备好后立即发送事件。
缺点是通信是单向的,客户端无法通知服务是否成功处理了事件。由于缺少此反馈,服务可能很难控制事件发生率以防止客户端不堪重负。
创建 WebSocket 是为了实现任意双向通信,因此这是服务向客户端发送通知的可行选项。客户端还可以将处理确认发送回服务。
WebSocket 已经存在了一段时间,应该得到许多框架和语言的支持。WebSocket 连接以 HTTP 1.1 连接开始,因此许多负载均衡器和反向代理应该支持基于 HTTPS 的 WebSocket。
WebSocket 通常与浏览器和移动客户端一起使用,但很少用于服务到服务的通信。
gRPC 与 WebSocket 类似,都支持任意双向通信。gRPC 的优点在于它以协议和消息格式定义文件为中心。这些文件用于生成对于客户端和服务开发人员至关重要的代码。
gRPC 用于服务到服务的通信,并且支持带有 grpc-web 的浏览器客户端。
gRPC 支持多种流行的编程语言和平台,但支持范围比 HTTP 窄。
gRPC 在 HTTP/2 之上工作,这可能会导致 TLS 终止等反向代理和负载均衡器出现困难。
最后,服务和客户端可以使用消息队列作为通知的传递机制。服务将通知放入队列中,客户端从队列中接收通知。队列可以由 RabbitMQ、Kafka、Celery、Google PubSub、Amazon SQS 等众多系统之一提供。具有不同属性的排队系统有多种选择,选择一个本身就是一个挑战。例如,还可以使用数据库来模拟队列。
必须在服务和拥有队列的客户端(即谁为其付费)之间做出决定。无论哪种方式,每当服务需要向其推送通知时,排队系统和队列都应该可用,否则通知将丢失(除非服务使用另一个队列在内部缓冲它们)。
队列通常用于服务到服务的通信,但某些技术也允许浏览器作为客户端。
值得注意的是,在上面列出的其他选项中,可能会在服务端使用“隐式”内部队列。原因之一是防止在没有客户端可以接收通知时丢失通知。还有许多其他好的理由,例如让客户端按照自己的节奏处理通知、允许最大化处理吞吐量、允许以固定容量处理尖峰流量。
在此选项中,队列“显式”用作传递机制,即服务不会将任何其他机制(HTTP、gRPC 或 WebSocket 端点)放在队列前面,并让客户端直接从队列接收通知。
消息传递在组织微服务通信中很流行。
在所有选项中,必须确定服务、客户端和企业是否可以容忍通知丢失。如果由于处理错误、不可用等原因丢失通知是可以接受的,那么可以选择一些更简单的技术。
从服务端监控客户端处理错误是很有价值的。这样,服务所有者就可以知道哪些客户的问题更严重,而无需询问他们。
如果使用队列(隐式或显式),则监视队列的长度和最旧通知的寿命很有价值。它可以让服务所有者判断客户端中数据的陈旧程度。
如果通知的传递以仅在客户端成功处理后通知才被删除的方式组织,则当客户端无法处理它时,相同的通知可能会陷入无限接收循环。这种通知有时被称为“有毒消息”。有毒消息应由服务或排队系统删除,以防止客户端陷入无限循环。常见的做法是将有害消息移动到一个特殊的位置,有时称为“死信队列”,以供以后的人为干预。
| 归档时间: |
|
| 查看次数: |
1342 次 |
| 最近记录: |