微服务:如何跟踪失败的服务?

Microservices: how to track fallen down services?

问题: 假设有两个服务 A 和 B。服务 A 对服务 B 进行 API 调用。 一段时间后,服务A由于网络错误而崩溃或丢失。

另一个服务如何猜测来自服务 A 的出站呼叫丢失/从未发生?我需要另一个并发应用程序,如果服务 A outbound CALL 丢失,它会自动做出反应(运行 紧急代码)。

有哪些前沿的解决方案?

我的想法,例如:

  1. 服务 A 在某些中间件中注册了一个调用事件(事件信息、“运行ning”状态、时间戳等)。
  2. 如果此调用在 N 秒后仍未完成,中间件中的某些“调用超时”事件会自动启动紧急代码。
  3. 如果调用在适当的时间完成,服务 A 在同一中间件中将调用状态标记为“已完成”,紧急代码将不会是 运行。

P.S。我在 Java 堆栈上。

谢谢!

有很多方法可以解决您的问题。

我猜你是在谈论微服务中的设计模式和 Cicruit Breaker 这两个话题

https://dzone.com/articles/design-patterns-for-microservices

为了解决您的问题,通常我会在服务之间放置一个消息队列,并使用服务发现来检测哪个服务处于活动状态,如果您的服务死机或过载,则使用 Cicruit Breaker 方法

我建议研究 RetryTimeoutCircuit Breaker 等模式回退健康检查。或者,如果您关心并发调用和故障隔离,您也可以查看 Bulkhead 模式。 有许多资源解释了这些众所周知的模式,例如:

我不知道您使用的是哪个技术堆栈,但通常已经有一些功能可以解决这些问题,前提是您已经可以将这些功能整合到您的解决方案中。有些库已经处理了这种弹性功能,例如,您可以对其进行设置,以便在重试失败、超时、断路器激活等事件发生时执行您的自定义代码。

例如对于 Java 堆栈 Hystrix is widely used, for .Net you can look into Polly .Net 使用重试、超时、断路器、隔板或回退功能。

关于健康检查,您可以查看 Actuator for Java 并且 .Net 核心已经提供了一个 health check middleware,或多或少地提供了该功能开箱即用。

但在使用任何库之前,我建议首先熟悉所列模式的目的和概念,以选择和集成最适合您的用例和主要关注点的模式。

更新

我们必须在这里区分两个众所周知的问题:

1.) 服务 A 如何稳健地处理服务 B 的临时中断(或服务 A 和 B 之间的网络连接归结为相同的问题)?

为了解决相关问题,上述模式会有所帮助。

2.) 如果服务A本身宕机,如何保证本应发送给服务B的请求不会丢失?

要解决此类问题,手头有不同的选择。

2a.) 向服务 A 执行请求(然后触发服务 B)的组件也应用提到的弹性模式,并将重试其请求,直到服务 A 成功回答它已执行其任务(其中还包括对服务 B 的成功请求)。

每个服务也可以有多个实例,这些实例前面有某种负载均衡器,这些实例会将请求分发并定向到特定服务的可用实例(基于定期执行的健康检查)。或者您可以使用服务注册表(请参阅 https://microservices.io/patterns/service-registry.html)。

您当然可以将多个 API 调用串联起来,但这可能会导致级联故障。因此,我宁愿采用 异步通信方法 ,如下一个选项所述。

2b.) 让我们考虑一下,服务 A 的某个实例将可靠地执行对服务 B 的请求至关重要。

在这种情况下,您可以使用消息队列,如下所示:

  • 假设您有一个队列,其中收集了服务 A 执行的作业。
  • 那么您有多个服务 A 实例 运行(请参阅水平缩放),其中每个实例将使用相同的队列。
  • 您将使用消息队列服务的消息锁定 功能,确保一旦服务 A 的一个实例从队列中读取消息,其他实例将看不到它。如果服务 A 能够完成它的工作(即调用服务 B,在服务 A 的持久性中保存一些状态以及成功处理所需的任何其他任务),它将在之后从队列中删除消息 因此服务 A 的其他实例也不会处理相同的消息。
  • 如果服务 A 在处理过程中出现故障,队列服务将自动为您解锁消息,并且服务 A 的另一个实例 A(或重新启动后的同一实例)将尝试读取消息(即作业) 从队列中尝试执行所有任务(调用服务 B 等)

您可以组合多个队列,例如还可以异步向服务 B 发送消息,而不是直接对其执行某种 API 调用。

要注意的是,队列服务是一些高度可用的冗余服务,它已经确保消息一旦发布到队列就不会丢失。

当然,您也可以处理要在您自己的服务 A 数据库中执行的作业,但考虑到当服务 A 收到请求时,总是有机会 在将作业的状态保存到它的持久性存储以供以后处理之前关闭。如果仔细选择并正确使用,队列服务已经为您解决了这个问题。

例如,如果将 Kafka 视为消息服务,您可以查看与使用此特定技术时的问题解决方案相关的堆栈溢出答案: