[spring-projects/spring-boot]添加对健康指标组的支持

2024-04-23 687 views
9

我的组织使用在 Kubernetes 环境中运行的 Spring Boot 服务。 Kubernetes 有两种类型的健康探针的概念:活性探针和就绪探针。活动探针用于检测服务是否需要重新启动,而就绪探针则检测服务是否准备好添加到负载均衡器。

如果有一种方法可以对就绪性和活跃性执行不同的检查,那就太好了。例如,如果应用程序暂时失去与其数据源的连接,则应将其从负载平衡中删除,但不需要重新启动。因此,我们希望将其包含DataSourceHealthIndicator在就绪探针中,但不包含活性探针。

请注意,https://github.com/spring-projects/spring-boot/issues/8685已经允许查询单个健康指标,但不允许查询多个指标。也许 Spring Boot 可以提供一种配置命名指标组的方法,这些指标可以作为/actuator/health/{group}.

回答

2

我正在研究这个问题。我将在这个周末发送 PR。

4

谢谢你,@eddumelendez。本周早些时候我们讨论这个问题时有一些想法。它们只是初步的想法,所以它们不是规定性的,但希望它们会有一些用处。

我们设想了一个基于财产的机制来创建一组健康指标。在 中application.properties,属性的值可能是要包含在组中的健康指标的 id 的逗号分隔列表。关键是该组的名称。

然后我们认为它CompositeHealthIndicator可以与组中的所有健康指标一起重复使用和创建。 @philwebb 发现的一个问题是,这些代表一个组的综合运行状况指标需要在主要运行状况检查中隐藏,否则任何分组指标都将在一次检查中被调用两次。

4

感谢您让我知道@wilkinsona,我将考虑这些想法来解决这个问题。

0

@eddumelendez 你还有兴趣从事这方面的工作吗?如果没有,我们将继续拾取它。

5

@mbhave 我有一个初稿,我可以在今天晚些时候分享。

6

万分感谢!

7

抱歉耽搁了。我已提交#16252

2

我对此进行了一些思考,我想知道我们是否不应该有“开箱即用组”的概念,就像我们有日志记录组一样。两个明显的就是在这个请求中定义的。

如果给定指标有明显的默认值,那么与让用户在每个应用程序中配置指标列表相比,有一种方法来指示偏好将为用户提供更好的开箱即用体验。

另一个论点是,在这种情况下,如果未指定指标,则不会调用它。添加新指标时,没有任何提示,如果应用程序仅依赖一组或另一组,则很容易错过。

当然,我们可以从可配置的地图开始,看看如何改进手动解决方案。

4

我已经开始破解一个原型,其中应用了以下配置(基于随机指标spring-boot-sample-actuator):

management.health.groups.ready=diskSpace
management.health.groups.live=example,hello,db,unknown

(请注意该unknown指标代表了对不可用指标的引用这一事实)。基于该分支启动样本会为该ready组提供以下结果:

// http://localhost:8080/actuator/health/ready

{
  "status": "UP",
  "details": {
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 499963170816,
        "free": 271707451392,
        "threshold": 10485760
      }
    }
  }
}

live组如下所示:

// http://localhost:8080/actuator/health/live

{
  "status": "UP",
  "details": {
    "example": {
      "status": "UP",
      "details": {
        "counter": 42
      }
    },
    "hello": {
      "status": "UP",
      "details": {
        "hello": "world"
      }
    },
    "db": {
      "status": "UP",
      "details": {
        "database": "H2",
        "result": 1,
        "validationQuery": "SELECT 1"
      }
    }
  }
}

主端点不包含任何对组的引用,live或者ready组会被注册表实现自动过滤掉。

这个峰值相当有限,但带来了几个有趣的问题:

  • 运行状况指示器不能与组同名。如果尝试使用指标名称创建一个组,它(应该)会失败,因为当前的想法是查询/health/{name}组和单个指标。
  • 如果配置引用未知指标会发生什么?现在它被忽略了
  • 如果组是空的会发生什么?用例:其所有指标都不可用或组未定义任何指标。现在它返回了UNKNOWN
  • getAll()当前的实现是相当糟糕的,因为它依赖于不返回组的事实。然而,javadoc 提到应该返回指示符。这会将这些组变成一个实现细节,因此也许我们想给它们更多的可见性
  • 指标必须一一列出。如果在此过程中添加新指标,这会很乏味且容易出错
  • 鉴于组的配置方式,groups是一个保留字,即您不能以这种方式命名指标。
2

运行状况指示器不能与组同名。如果尝试使用指标名称创建一个组,它(应该)会失败,因为当前的想法是查询 /health/{name} 以获取组和单个指标。

失败+1

如果配置引用未知指标会发生什么?现在它被忽略了

组中意外缺少指示器可能会导致流量在实例无法处理时被路由到实例。因此,我认为实施应该尽力帮助用户避免这种错误。我认为如果一个组配置为包含未知指标,我们应该会失败。我们可以考虑一个配置选项,将这种行为从失败更改为忽略。

如果组是空的会发生什么?用例:其所有指标都不可用或组未定义任何指标。现在它返回 UNKNOWN

我认为未知的指标应该会导致失败(见上文)。我认为UNKNOWN如果没有指标,一个真正空的组(而不是只包含不存在的指标)应该像今天的主要端点一样返回。

当前的实现相当糟糕,因为它依赖于 getAll() 不返回组的事实。然而,javadoc 提到应该返回指示符。这会将这些组变成一个实现细节,因此也许我们想给它们更多的可见性

我同意你的感觉,这不太正确,但我不确定我现在有更好的建议。组指示器的两个标记界面感觉有点异味,但我不确定我们还能如何区分组指示器和标准指示器。

指标必须一一列出。如果在此过程中添加新指标,这会很乏味且容易出错

我想知道我们是否需要某种通配符或模式匹配支持。如果没有这个,我很想发布第一次迭代,并在我们做任何更复杂的事情之前收集一些反馈。

鉴于组的配置方式,组是保留字,即您不能以这种方式命名指示器。

虽然这将是一个重大变化,但我认为不太可能有人实现了自己的名为 的指标groups。如果需要的话,我们应该允许关闭团体支持来避免潜在的问题。

8

@wilkinsona 和我对我一直在做的事情进行了集思广益,并将注册表中的组作为一个单独的实体来处理,这增加了很多负担。

如果将这些组作为常规指标来处理而无需对注册表进行特殊了解,那就更好了。那么,剩下的“唯一”问题是,在渲染“所有”指标时,相同的信息会出现多次。但这是一个问题吗?拥有一个显示群组及其结构的全局结构可能是一件非常好的事情。

如果我们同意这是一个值得追求的方向,我们需要确保特定指标仅被调用一次(出于性能原因,更重要的是,为了确保输出一致)。

Health已经是不可变的,所以听起来像是我们可以升级的东西,CompositeHealthIndicator或者是复合材料可能具有与注册表上的名称冲突的嵌套指示器的东西。

7

这是一个非常有趣的主题,在我们的组织中,我们需要将就绪端点和活动端点分开,正如上面提到的其他端点一样。

但我们走了一个有点不同的方向..我们引入了一个新的@Endpoint称为alive.该端点正在调用实际的Health.如果运行状况与 DOWN 不同,则返回 200 状态代码。例如,我们有以下健康状态和 http 响应状态的映射

健康 /actuator/alive http状态代码
UP 200
OUT_OF_SERVICE 200
CUSTOM_HEALTH_STATUS 200
DOWN 503

我们映射了 k8s 端点,例如:

  • livenessProbe->/actuator/alive
  • readinessProbe->/actuator/health

就连健康状况的措辞也和 kubernetes 的措辞有点相似,但有点颠倒,

  • livenessProbe->NOT DOWN
  • readinessProbe->NOT OUT_OF_SERVICE

当资源不可用时,所有核心运行状况指标都已返回 DOWN,例如数据源、jms 等*。这满足了我们的需求这使我们能够编写自己的健康指标并返回OUT_OF_SERVICE(一个例子是长缓存加载操作)。

我知道这与所要求的方法完全不同,但我认为它提供了所要求的内容以及一些可能有用的机会。

例如,某些运行状况指示器可能会以更智能的方式编写,以区分应用程序是否应该停止运行,或者应该停止服务请求。一个示例(尽管可能难以实现)是区分 datasourceHealthIndicator 上的瞬时错误并OUT_OF_SERVICE从其他错误中返回,然后返回DOWN.

我不知道这种方法是否比分组健康指标更强大,但新方法的实施alive @Endpoint非常简单,而且它给了我们我们所需要的东西。我能想到的一个好处是,通过这种方法,单个运行状况指示器可以决定事件是否需要在 k8s 中停止/重新启动,或者只是无法使用。所以对于上面提到的缓存加载的例子,我们的规则是如果缓存没有加载返回OUT_OF_SERVICE并且不提供流量,如果缓存加载失败返回DOWN,重启容器,然后重试。

实际上这种方法可以很好地与组结合使用,我想说的是,虽然“自定义”端点很容易编写,但我希望在 spring-boot 中看到类似的东西:)

或者它也可以取代分组方法,因为我们不需要配置组,而是只需配置从每个运行状况指示器返回的状态,例如,使用属性或代码来控制会很容易,jmsHealthIndicator 应该返回OUT_OF_SERVICE而不是DOWN

这样,除了使用的措辞之外,我们最终还可以给出OUT_OF_SERVICEAND 的语义。DOWN

如果这有意义并且涵盖了所需的内容,我很乐意提交拉取请求。

附带说明一下,在某些情况下,有些人可能希望健康指标既不参与活跃度也不参与就绪结果,而仅使用 if 来进行警报目的,对于这些情况,我们可以只使用称为的自定义健康WARN状态导致健康状况和就绪端点的 200 http 状态..但我不知道这对更多人是否有意义。

为了完成我的长篇文章,我认为最好的方法是 Spring Boot 支持一组级联端点,例如

  • /actuator/health目前有效
  • /actuator/health/alive只关心至少一个DOWN状态是否存在
  • /actuator/health/ready只关心至少一个 OUT_OF_SERVICE状态是否存在

以及配置指示器故障状态的方法

我认为这种方法的其他一些好处是:

  • 所需的代码库比提交的拉取请求小得多
  • 拥有一组固定的端点(而不是/actuator/health/groupA特定于应用程序的端点)可以更轻松地配置下游工具,例如负载均衡器、客户端发现注册表等
  • 消除了指标不参与任何组的问题
3

感谢您分享您的想法,@ckoutsouridis。

我们上周讨论了这个问题,我们的感觉是自定义状态映射可能非常适合您的使用,而不是我们认为普遍适用的东西。您可能已经知道,您可以使用HealthAggregator和执行自定义映射HealthStatusHttpMapper

拥有一组固定的端点(而不是 /actuator/health/groupA,这是特定于应用程序的)更容易配置下游工具,例如负载均衡器、客户端发现注册表等

活性和就绪检查只是我们希望通过能够对健康指标进行分组来涵盖的用例之一。我们希望提供足够灵活的东西来支持 Kubernetes 的活跃性和就绪性检查以及更通用的分组。

消除了指标不参与任何组的问题

如果我们采用与我们目前的想法类似的东西,这将不是问题。每个指标都将包含在其中/actuator/health,可以将其视为包含每个健康指标的隐式组。

7

谢谢@wilkinsona 的回复。我并没有过多提及自定义状态,因为我们也很少使用它们。

OUT_OF_SERVICE我主要指的是这样一个事实,即目前DOWN健康状态没有真正的区别,如果 spring-boot 提供一个可以区分这些的端点,那将会非常强大。然后每个用户/开发人员都可以选择每个指示器在失败时应返回的状态(或保留默认值down)。

但是,是的,这个建议与将它们分组无关。

1

我们需要更新 Actuator API 文档以反映新功能。目前,它提到了组件和组件实例,但没有提到组。我们还需要弄清楚如何记录现在支持的任意深度路径。