2 回答
TA贡献1844条经验 获得超8个赞
客户端向服务器 A 发起 HTTP 调用。
服务器 A 创建一个唯一密钥并将其存储在缓存服务器中 60 秒(超时期限)。
服务器 A 通过 HTTP 调用将请求转发给 B。这是对 B 的一次火灾和忘记呼叫。
服务器 A 立即使用唯一密钥向客户端返回响应。
客户端开始使用 GET 状态 HTTP API 进行轮询(假设每 500 毫秒一次),以检查服务器 A 是否已完成处理。
意味着当服务器 B 完成任务并通过 HTTP API 回调到服务器 A 时。
服务器 A 将针对唯一键的响应短时间(比如 60 秒)存储在缓存中。
客户端调用 A 进行状态检查 API 将从缓存中获取数据并将其返回给客户端。
除了服务器A和服务器B之外没有其他组件。有缓存服务器,但它是A内部的。B不需要知道它。哪个服务维护什么,定义明确,维护方便。B 可以有内部队列来处理转发的请求。但是 A 不需要知道 B 是如何实现的。每个服务都可以由不同的团队使用简单的 HTTP 合同进行维护。
另一个优点是客户端不持有与 A 的长时间运行的 HTTP 连接。我提到了轮询,认为大多数原始客户端就像一个旧浏览器。如果您的客户端支持网络套接字,您可以使用它从 A 发送回响应。无论如何,这里的轮询会执行得很好,因为状态检查 API 只是一个缓存调用,没有别的。
有人会问,A&B之间server to server通信的重试逻辑在哪里?但是我们不需要为此排队。本质上,客户端在这里需要同步类型的调用。我们只是将其分解为多个调用。反正反应要快。我们可以在 HTTP 客户端有一个重试 3-5 次的重试机制,以防网络出现故障。我们正在使用豆荚。我希望它位于 k8s 负载均衡器之后。如果第一个 pod 出现故障,该负载均衡器无论如何都会重试一个健康的 pod。所以你几乎被覆盖在那里。
我不知道您的确切要求,我是在快速思考后写下这篇文章的。但总的来说,这对我来说很好。它会很健壮并且具有低延迟调用。也许这里和那里几乎不需要调整。
根据评论更新:
客户端轮询的实现非常基础。基本上,它是一个循环,每 0.5 秒运行一次并进行 HTTP GET 调用以检查状态。我相信任何客户都应该能够实现这一点。但是可能会有一些限制,客户团队不想进行任何更改。我不想进入那个。
假设一些客户端没有轮询功能,一些客户端不支持网络套接字。然后唯一剩下的就是客户端和服务器 A 之间的长期 HTTP 连接。假设我们采用这种方法。不讨论在服务器中有一个长期存在的请求线程的明显问题,还有另一个特定于这种情况的问题。服务器B回调时需要通知服务器A中的请求线程。
方法 1:我们使用其他答案中提到的某种队列(我会将队列保留在 A 内部,但我们不要深入讨论)。我们使用一些消息键,以便消费发生在同一个 pod 中。我们的 API 和消费者都在同一个 pod 中运行。这是可以实现的。但是请求线程和消费者线程是不同的。消费者线程需要以某种方式通知请求线程结果可用。所以需要一些线程间通信,这只会让事情变得复杂。这不是队列的责任。所以我会放弃这种方法。
有人会问,能不能请求线程直接监听一些带有特定key的消息。我不这么认为。为了争论的缘故,有一些队列技术可以做到这一点。但是您实际上是为每个无法扩展的键拥有短暂的消费者和单独的分区。监听所有消息并忽略临时消费者中的所有消息也不是一个可行的解决方案。
方法二:从方法一我们了解到,服务器B发送回调时需要通知请求线程。这会使整个流程变得非常复杂,并且您需要像 ZooKeeper 这样的额外组件来进行分布式锁定或监视某些更改。我们可以简单地扩展我们当前的系统来进行服务器端轮询,而不是那样做。由于我们已经在进行客户端轮询,因此响应时间不应有任何明显差异。我们只是将其移至服务器端。此外,无论如何,无论我们构建分布式通知还是服务器轮询,我们都必须在服务器 A 中维护长时间运行的请求线程。流程看起来像:
客户端调用服务器 A。
服务器 A 请求线程对服务器 B 进行 HTTP 调用。
服务器 B 开始后台处理并立即将 HTTP 响应返回给 A。
如果密钥每 500 毫秒可用,则 A 中的请求线程会在缓存服务器中启动循环检查。
B 处理结果并向 A 进行 HTTP 回调。
A 中的回调线程针对相同的键缓存结果。
原始请求线程从缓存中读取值并将响应返回给客户端。
在进行下一次缓存调用之前,原始请求线程将休眠 500 毫秒。所以其他线程将能够利用时间。此外,通过密钥获取缓存非常快,并且您不会在那里遇到任何可扩展性问题。
但是,如果您保持来自客户端的长时间运行的 HTTP 连接,您将更快地耗尽请求线程。因此,您将需要更多 A pod 来处理相同的请求率。我仍然建议与客户团队交谈并在那里进行必要的更改,以便使用短暂的连接(与您当前的流程相同)。
TA贡献1830条经验 获得超3个赞
问题描述
因此,假设您调用的服务器应用程序server_app有 3 个 pod:
+---------------------+
| server_app_service |
+---------------------+
| server_app_pod_a |
| server_app_pod_b |
| server_app_pod_c |
+---------------------+
您的服务收到一个名为 的请求"request A"
,并决定将其传递给server_app_pod_a
。现在您server_app_pod_a
将请求转发到某个网关,并等待某种通知,以继续处理客户端的响应。正如您所知,无法保证当网关执行 时request B
,服务会server_app_pod_a
再次将其传递给。即使这样做,您的应用程序的状态管理也将成为一项艰巨的任务。
讯息
您可能已经注意到,我在上一段中将“通知”一词加粗了,那是因为如果您认真考虑一下,它看起来request "B"
更像是带有某些消息的通知,而不是对某些资源的请求。所以我的第一选择是像 kafka 这样的消息队列(如你所知,有很多这样的消息队列)。这个想法是,如果你可以定义一个算法来计算你的请求的唯一键,你可以期待在你完全相同的 pod 中产生通知。这样,状态管理会简单很多,而且在同一个 pod 中获得通知的机会也会高很多(当然这取决于很多因素,比如消息队列的状态)。看看你的问题:
我想以最好的方式做到这一点,同时牢记缩放比例。
当然,您可以使用像 kafka 这样的消息队列来实现消息队列和您的应用程序的扩展和更少的数据丢失。
所有请求都会超时,使初始请求在 60 秒后超时。
这取决于您如何管理代码库中的超时,使用上下文是个好主意。
我还想知道如何用其他编程语言实现它。
使用消息队列是一个普遍的想法,它几乎适用于任何编程语言,但根据语言的编程范式、特定于语言的库和工具,可能会有一些其他方法来解决这个问题。例如Scala
,如果你使用一些特定的工具akka
(它提供了 actor 模型编程范例),你可以使用所谓的东西akka-cluster-sharding
, 来处理这个问题。这个想法很简单,我们知道必须有某种监管者,它知道自己订阅者的确切位置和状态。因此,当它收到一些消息时,它只知道将请求转发到何处以及将请求转发给哪个参与者(我们正在谈论参与者模型编程)。换句话说,它可以用于在集群上生成的参与者之间共享状态,无论是否在同一台机器上。但作为个人偏好,我不会选择特定语言的交流,而是会坚持笼统的想法,因为它可能会在未来引起问题。
包起来
足够长的解释:)。只是为了弄清楚我在说什么,让我们继续完全相同的场景,但通信模型有所不同:
客户端向服务发送请求“A”
server_app
。该服务选择其中一个 pod(
server_app_pod_b
例如)来处理请求。然后 pod 尝试为请求定义一些键,并将它与请求一起传递给网关,并等待带有键的消息在队列中发布。
网关做它应该做的事,并将带有密钥的消息发送到消息队列。
完全相同的 pod
serer_app_pod_b
接收到带有 key 的消息,获取消息的数据,并继续处理客户端的请求。
可能还有其他方法可以解决这个问题,但这就是我想要的。希望它有所帮助!
- 2 回答
- 0 关注
- 107 浏览
添加回答
举报