This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

使用弹性策略进行错误恢复

如何配置和自定义 Dapr 错误重试、超时和断路器

1 - 概览

配置 Dapr 重试、超时和熔断器

Dapr 提供了通过弹性规范定义和应用容错弹性策略的能力。弹性规范保存在与组件规范相同的位置,并在 Dapr 边车启动时应用。边车决定如何将弹性策略应用于您的 Dapr API 调用。

  • 在自托管模式下: 弹性规范必须命名为 resiliency.yaml
  • 在 Kubernetes 中: Dapr 会查找应用程序使用的命名弹性规范。

策略

您可以通过以下部分配置 Dapr 弹性策略:

  • 定义策略应用位置的元数据(如命名空间和作用域)
  • 指定弹性名称和行为的策略,例如:
  • 确定这些策略作用于哪些交互的目标,包括:

定义完成后,您可以使用以下命令将此配置应用到本地 Dapr 组件目录或 Kubernetes 集群:

kubectl apply -f <resiliency-spec-name>.yaml

此外,您还可以将弹性策略限定为特定应用

参见已知限制

弹性策略结构

以下是弹性策略的通用结构:

apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
  name: myresiliency
scopes:
  # 可选:将策略限定为特定应用
spec:
  policies:
    timeouts:
      # 超时策略定义

    retries:
      # 重试策略定义

    circuitBreakers:
      # 熔断器策略定义

  targets:
    apps:
      # 此处填写应用及其应用的策略

    actors:
      # 此处填写 actor 类型及其应用的策略

    components:
      # 此处填写组件及其应用的策略

完整示例策略

apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
  name: myresiliency
# 与订阅和配置规范类似,scopes 列出了可使用此弹性规范的 Dapr 应用 ID。
scopes:
  - app1
  - app2
spec:
  # policies 是定义超时、重试和熔断器策略的地方。
  # 每个策略都有一个名称,以便从弹性规范的 targets 部分引用它们。
  policies:
    # timeouts 是简单的命名持续时间。
    timeouts:
      general: 5s
      important: 60s
      largeResponse: 10s

    # retries 是重试配置的命名模板,在操作生命周期内实例化。
    retries:
      pubsubRetry:
        policy: constant
        duration: 5s
        maxRetries: 10

      retryForever:
        policy: exponential
        maxInterval: 15s
        maxRetries: -1 # 无限期重试

      important:
        policy: constant
        duration: 5s
        maxRetries: 30

      someOperation:
        policy: exponential
        maxInterval: 15s

      largeResponse:
        policy: constant
        duration: 5s
        maxRetries: 3

    # circuit breakers 会自动为每个组件和应用实例实例化。
    # 熔断器维护的计数器在 Dapr 边车运行期间一直存在。它们不会被持久化。
    circuitBreakers:
      simpleCB:
        maxRequests: 1
        timeout: 30s 
        trip: consecutiveFailures >= 5

      pubsubCB:
        maxRequests: 1
        interval: 8s
        timeout: 45s
        trip: consecutiveFailures > 8

  # targets 是命名策略应用到的对象。Dapr 支持 3 种目标类型 - 应用、组件和 actor
  targets:
    apps:
      appB:
        timeout: general
        retry: important
        # 服务的熔断器按应用实例限定作用域。
        # 当熔断器触发时,该路由会在配置的 `timeout` 期限内从负载均衡中移除。
        circuitBreaker: simpleCB

    actors:
      myActorType: # 自定义 Actor 类型名称
        timeout: general
        retry: important
        # actor 的熔断器按类型、id 或两者限定作用域。
        # 当熔断器触发时,该类型或 id 会在配置的 `timeout` 期限内从位置表中移除。
        circuitBreaker: simpleCB
        circuitBreakerScope: both ## 
        circuitBreakerCacheSize: 5000

    components:
      # 对于状态存储,策略应用于保存和检索状态。
      statestore1: # 任何组件名称 -- 这里恰好是一个状态存储
        outbound:
          timeout: general
          retry: retryForever
          # 组件的熔断器按每个组件配置/实例限定作用域。例如 myRediscomponent。
          # 当此熔断器触发时,在配置的 `timeout` 期限内将阻止与该组件的所有交互。
          circuitBreaker: simpleCB

      pubsub1: # 任何组件名称 -- 这里恰好是一个 pubsub broker
        outbound:
          retry: pubsubRetry
          circuitBreaker: pubsubCB

      pubsub2: # 任何组件名称 -- 这里恰好是另一个 pubsub broker
        outbound:
          retry: pubsubRetry
          circuitBreaker: pubsubCB
        inbound: # inbound 仅适用于从边车到应用的传递
          timeout: general
          retry: important
          circuitBreaker: pubsubCB

限制

  • 通过 gRPC 进行服务调用: 目前,通过 gRPC 进行的服务调用不支持弹性策略。

演示

观看此视频了解如何使用弹性

了解有关如何使用 Dapr 编写弹性微服务的更多信息。

后续步骤

了解有关弹性策略和目标的更多信息:

相关链接

尝试其中一个弹性快速入门:

2 - 弹性策略

为超时、重试和断路器配置弹性策略

policies 下定义超时、重试和断路器策略。每个策略都有一个名称,以便您可以从弹性规范中的 targets 部分引用它们。

2.1 - 超时弹性策略

配置超时的弹性策略

网络调用可能因多种原因失败,导致应用程序无限期等待响应。通过设置超时时长,您可以切断那些无响应的服务,释放资源以处理新请求。

超时是可选策略,可用于提前终止长时间运行的操作。设置一个现实的超时时长,反映生产环境中的实际响应时间。如果超出了超时时长:

  • 正在进行的操作将被终止(如果可能)。
  • 返回错误。

超时策略格式

spec:
  policies:
    # 超时是指定的时长。
    timeouts:
      timeoutName: timeout1
      general: 5s
      important: 60s
      largeResponse: 10s

规范元数据

| 字段 | 详情 | 示例 | | timeoutName | 超时策略的名称 | timeout1 | | general | 标记为"general"的超时时长。使用 Go 的 time.ParseDuration 格式。没有设置最大值。 | 15s2m1h30m | | important | 标记为"important"的超时时长。使用 Go 的 time.ParseDuration 格式。没有设置最大值。 | 15s2m1h30m | | largeResponse | 等待大型响应的超时时长。使用 Go 的 time.ParseDuration 格式。没有设置最大值。 | 15s2m1h30m |

如果未指定超时值,策略不会强制执行时间,默认为您根据请求客户端设置的值。

后续步骤

相关链接

试用其中一个弹性快速入门:

2.2 - 重试和退避弹性策略

配置重试和退避的弹性策略

2.2.1 - 重试弹性策略

为重试配置弹性策略

请求可能因瞬时错误而失败,例如遇到网络拥塞、重新路由到过载实例等。有时,请求也可能因已设置的其他弹性策略而失败,例如触发了定义的超时或熔断器策略。

在这些情况下,配置 retries 可以:

  • 将相同请求发送到不同的实例,或
  • 在条件清除后重试发送请求。

重试和超时协同工作,超时确保您的系统在需要时快速失败,而重试从临时故障中恢复。

Dapr 提供默认弹性策略,您可以使用用户定义的重试策略覆盖它们。

重试策略格式

示例 1

spec:
  policies:
    # Retries are named templates for retry configurations and are instantiated for life of the operation.
    retries:
      pubsubRetry:
        policy: constant
        duration: 5s
        maxRetries: 10

      retryForever:
        policy: exponential
        maxInterval: 15s
        maxRetries: -1 # Retry indefinitely

示例 2

spec:
  policies:
    retries:
      retry5xxOnly:
        policy: constant
        duration: 5s
        maxRetries: 3
        matching:
          httpStatusCodes: "429,500-599" # retry the HTTP status codes in this range. All others are not retried. 
          gRPCStatusCodes: "1-4,8-11,13,14" # retry gRPC status codes in these ranges and separate single codes.

规范元数据

以下重试选项是可配置的:

重试选项描述
policy确定退避和重试间隔策略。有效值为 constantexponential
默认为 constant
duration确定重试之间的时间间隔。仅适用于 constant 策略。
有效值为 200ms15s2m 等格式。
默认为 5s
maxInterval确定exponential 退避策略可以增长到的重试之间的最大间隔。
额外的重试总是在 maxInterval 的持续时间后发生。默认为 60s。有效值为 5s1m1m30s 等格式。
maxRetries要尝试的最大重试次数。
-1 表示无限次重试,而 0 表示不会重试请求(本质上表现为未设置重试策略)。
默认为 -1
matching.httpStatusCodes可选:逗号分隔的要重试的 HTTP 状态码或状态码范围字符串。未列出的状态码不会被重试。
有效值:100-599,参考
格式:<code> 或范围 <start>-<end>
示例:“429,501-503”
默认:空字符串 "" 或未设置字段。重试所有 HTTP 错误。
matching.gRPCStatusCodes可选:逗号分隔的要重试的 gRPC 状态码或状态码范围字符串。未列出的状态码不会被重试。
有效值:0-16,参考
格式:<code> 或范围 <start>-<end>
示例:“4,8,14”
默认:空字符串 "" 或未设置字段。重试所有 gRPC 错误。

指数退避策略

指数退避窗口使用以下公式:

BackOffDuration = PreviousBackOffDuration * (Random value from 0.5 to 1.5) * 1.5
if BackOffDuration > maxInterval {
  BackoffDuration = maxInterval
}

重试状态码

当应用程序跨越多个服务时,特别是在像 Kubernetes 这样的动态环境中,服务可能因各种原因而消失,网络调用可能会开始挂起。状态码提供了对我们操作的洞察以及它们在生产中可能失败的位置。

HTTP

下表包含您可能收到的一些 HTTP 状态码示例,以及您是否应该或不应该重试某些操作。

HTTP 状态码建议重试?描述
404 Not Found❌ 否资源不存在。
400 Bad Request❌ 否您的请求无效。
401 Unauthorized❌ 否尝试获取新凭据。
408 Request Timeout✅ 是服务器等待请求超时。
429 Too Many Requests✅ 是(如果存在,请遵守 Retry-After 头)。
500 Internal Server Error✅ 是服务器遇到意外情况。
502 Bad Gateway✅ 是网关或代理接收到无效响应。
503 Service Unavailable✅ 是服务可能会恢复。
504 Gateway Timeout✅ 是临时网络问题。

gRPC

下表包含您可能收到的一些 gRPC 状态码示例,以及您是否应该或不应该重试某些操作。

gRPC 状态码建议重试?描述
Code 1 CANCELLED❌ 否N/A
Code 3 INVALID_ARGUMENT❌ 否N/A
Code 4 DEADLINE_EXCEEDED✅ 是使用退避重试
Code 5 NOT_FOUND❌ 否N/A
Code 8 RESOURCE_EXHAUSTED✅ 是使用退避重试
Code 14 UNAVAILABLE✅ 是使用退避重试

基于状态码的重试过滤器

重试过滤器允许通过指定应应用重试的 HTTP 和 gRPC 状态码或范围,对重试策略进行细粒度控制。

spec:
  policies:
    retries:
      retry5xxOnly:
        # ...
        matching:
          httpStatusCodes: "429,500-599" # retry the HTTP status codes in this range. All others are not retried. 
          gRPCStatusCodes: "4,8-11,13,14" # retry gRPC status codes in these ranges and separate single codes.

演示

观看 Diagrid 的 Dapr v1.15 庆祝活动期间演示的演示,了解如何使用 Diagrid Conductor 设置重试状态码过滤器。

后续步骤

相关链接

尝试其中一个弹性快速入门:

2.2.2 - 覆盖默认重试弹性策略

了解如何为特定 API 覆盖默认重试弹性策略

Dapr 为任何不成功的请求(如故障和临时错误)提供默认重试。在弹性规范中,您可以通过使用保留的命名关键字定义策略来覆盖 Dapr 的默认重试逻辑。例如,定义一个名为 DaprBuiltInServiceRetries 的策略可以覆盖通过服务间请求在边车之间发生故障时的默认重试。策略覆盖不会应用于特定目标。

注意:虽然您可以使用更稳健的重试来覆盖默认值,但不能使用低于提供的默认值的值进行覆盖,也不能完全移除默认重试。这可以防止意外的停机。

下表描述了 Dapr 的默认重试以及用于覆盖它们的策略关键字:

功能覆盖关键字默认重试行为描述
服务调用DaprBuiltInServiceRetries每次调用重试的退避间隔为 1 秒,最多重试 3 次。边车对边车的请求(服务调用方法调用)失败并导致 gRPC 代码 UnavailableUnauthenticated
ActorsDaprBuiltInActorRetries每次调用重试的退避间隔为 1 秒,最多重试 3 次。边车对边车的请求(Actor 方法调用)失败并导致 gRPC 代码 UnavailableUnauthenticated
Actor 提醒DaprBuiltInActorReminderRetries每次调用重试采用指数退避,初始间隔为 500ms,最大间隔为 60s,持续时间为 15 分钟向状态存储持久化 Actor 提醒失败的请求
初始化重试DaprBuiltInInitializationRetries每次调用执行 3 次重试,采用指数退避,初始间隔为 500ms,持续时间为 10 秒向应用程序发出请求以检索给定规范时的故障。例如,检索订阅、组件或弹性规范失败

下面的弹性规范示例展示了如何使用保留的命名关键字 ‘DaprBuiltInServiceRetries’ 来覆盖_所有_服务调用请求的默认重试。

同时还定义了一个名为 ‘retryForever’ 的重试策略,该策略仅应用于 appB 目标。appB 使用 ‘retryForever’ 重试策略,而所有其他应用程序服务调用重试故障则使用被覆盖的 ‘DaprBuiltInServiceRetries’ 默认策略。

spec:
  policies:
    retries:
      DaprBuiltInServiceRetries: # 覆盖服务间调用的默认重试行为
        policy: constant
        duration: 5s
        maxRetries: 10

      retryForever: # 用户定义的重试策略会替换默认重试。目标仅依赖于应用的策略。 
        policy: exponential
        maxInterval: 15s
        maxRetries: -1 # 无限期重试

  targets:
    apps:
      appB: # 目标服务的 app-id
        retry: retryForever

相关链接

尝试其中一个弹性快速入门:

2.3 - 断路器弹性策略

为断路器配置弹性策略

当其他应用程序/服务/组件出现较高的故障率时,会使用断路器策略。断路器通过监控请求并在满足特定条件时切断到受影响服务的所有流量来减少负载。

在一定数量的请求失败后,断路器会"跳闸"或打开以防止级联故障。通过这样做,断路器为服务提供从故障中恢复的时间,而不是用事件淹没它。

断路器还可以进入"半开"状态,允许部分流量通过以查看系统是否已恢复。

一旦请求恢复成功,断路器将进入"关闭"状态,并允许流量完全恢复。

断路器策略格式

spec:
  policies:
    circuitBreakers:
      pubsubCB:
        maxRequests: 1
        interval: 8s
        timeout: 45s
        trip: consecutiveFailures > 8

规格元数据

重试选项描述
maxRequests断路器处于半开状态(从故障中恢复)时允许通过的最大请求数。默认为 1
interval断路器用于清除其内部计数的循环时间周期。如果设置为 0 秒,则永远不会清除。默认为 0s
timeout打开状态的持续时间(直接在故障之后),直到断路器切换到半开状态。默认为 60s
trip由断路器评估的 Common Expression Language (CEL) 表达式。当表达式评估为 true 时,断路器跳闸并变为打开状态。默认为 consecutiveFailures > 5。其他可能的值是 requeststotalFailures,其中 requests 表示电路打开之前成功或失败调用的数量,totalFailures 表示电路打开之前失败尝试的总数(不一定是连续的)。示例:requests > 5totalFailures >3

后续步骤

相关链接

尝试以下弹性快速入门之一:

2.4 - 默认弹性策略

了解有关超时、重试和熔断器的默认弹性策略的更多信息

在弹性中,您可以设置具有广泛范围的默认策略。这是通过保留关键字完成的,让 Dapr 知道何时应用该策略。有 3 种默认策略类型:

  • DefaultRetryPolicy
  • DefaultTimeoutPolicy
  • DefaultCircuitBreakerPolicy

如果定义了这些策略,它们将用于对服务、应用程序或组件的每个操作。它们还可以通过附加额外关键字来修改以更加具体。具体策略遵循以下模式,Default%sRetryPolicyDefault%sTimeoutPolicyDefault%sCircuitBreakerPolicy。其中 %s 被替换为策略的目标。

下表列出了所有可能的默认策略关键字以及它们如何转换为策略名称。

关键字目标操作示例策略名称
App服务调用。DefaultAppRetryPolicy
ActorActor 调用。DefaultActorTimeoutPolicy
Component所有组件操作。DefaultComponentCircuitBreakerPolicy
ComponentInbound所有入站组件操作。DefaultComponentInboundRetryPolicy
ComponentOutbound所有出站组件操作。DefaultComponentOutboundTimeoutPolicy
StatestoreComponentOutbound所有状态存储组件操作。DefaultStatestoreComponentOutboundCircuitBreakerPolicy
PubsubComponentOutbound所有出站发布订阅(发布)组件操作。DefaultPubsubComponentOutboundRetryPolicy
PubsubComponentInbound所有入站发布订阅(订阅)组件操作。DefaultPubsubComponentInboundTimeoutPolicy
BindingComponentOutbound所有出站绑定(调用)组件操作。DefaultBindingComponentOutboundCircuitBreakerPolicy
BindingComponentInbound所有入站绑定(读取)组件操作。DefaultBindingComponentInboundRetryPolicy
SecretstoreComponentOutbound所有密钥存储组件操作。DefaultSecretstoreComponentTimeoutPolicy
ConfigurationComponentOutbound所有配置组件操作。DefaultConfigurationComponentOutboundCircuitBreakerPolicy
LockComponentOutbound所有锁组件操作。DefaultLockComponentOutboundRetryPolicy

策略层次解析

如果正在执行的操作匹配策略类型,并且没有针对它的更具体的策略,则会应用默认策略。对于每个目标类型(app、actor 和 component),优先级最高的策略是命名策略,即专门针对该构造的策略。

如果不存在,则策略从最具体到最广泛应用。

默认策略与内置重试如何协同工作

内置重试的情况下,默认策略不会阻止内置重试策略运行。两者一起使用,但仅在特定情况下。

对于服务和 Actor 调用,内置重试专门处理连接到远程边车时的问题(在需要时)。由于这些对 Dapr 运行时的稳定性很重要,因此它们不会被禁用,除非为操作专门引用了命名策略。在某些情况下,可能会同时从内置重试和默认重试策略进行额外重试,但这可以防止过于薄弱的默认策略降低边车的可用性/成功率。

应用程序的策略解析层次,从最具体到最广泛:

  1. App 目标中的命名策略
  2. 默认 App 策略 / 内置服务重试
  3. 默认策略 / 内置服务重试

Actor 的策略解析层次,从最具体到最广泛:

  1. Actor 目标中的命名策略
  2. 默认 Actor 策略 / 内置 Actor 重试
  3. 默认策略 / 内置 Actor 重试

组件的策略解析层次,从最具体到最广泛:

  1. 组件目标中的命名策略
  2. 默认组件类型 + 组件方向策略 / 内置 Actor 提醒重试(如果适用)
  3. 默认组件方向策略 / 内置 Actor 提醒重试(如果适用)
  4. 默认组件策略 / 内置 Actor 提醒重试(如果适用)
  5. 默认策略 / 内置 Actor 提醒重试(如果适用)

作为一个示例,考虑以下解决方案,其中包含三个应用程序、三个组件和两个 Actor 类型:

应用程序:

  • AppA
  • AppB
  • AppC

组件:

  • Redis Pubsub:pubsub
  • Redis statestore:statestore
  • CosmosDB Statestore:actorstore

Actor:

  • EventActor
  • SummaryActor

以下是使用默认和命名策略并将其应用于目标的策略。

spec:
  policies:
    retries:
      # 全局重试策略
      DefaultRetryPolicy:
        policy: constant
        duration: 1s
        maxRetries: 3
      
      # 应用程序的全局重试策略
      DefaultAppRetryPolicy:
        policy: constant
        duration: 100ms
        maxRetries: 5

      # 应用程序的全局重试策略
      DefaultActorRetryPolicy:
        policy: exponential
        maxInterval: 15s
        maxRetries: 10

      # 入站组件操作的全局重试策略
      DefaultComponentInboundRetryPolicy:
        policy: constant
        duration: 5s
        maxRetries: 5

      # 状态存储的全局重试策略
      DefaultStatestoreComponentOutboundRetryPolicy:
        policy: exponential
        maxInterval: 60s
        maxRetries: -1

     # 命名策略
      fastRetries:
        policy: constant
        duration: 10ms
        maxRetries: 3

     # 命名策略
      retryForever:
        policy: exponential
        maxInterval: 10s
        maxRetries: -1

  targets:
    apps:
      appA:
        retry: fastRetries

      appB:
        retry: retryForever
    
    actors:
      EventActor:
        retry: retryForever

    components:
      actorstore:
        retry: fastRetries

下表详细分解了在尝试调用此解决方案中的各种目标时应用哪些策略。

目标使用的策略
AppAfastRetries
AppBretryForever
AppCDefaultAppRetryPolicy / DaprBuiltInActorRetries
pubsub - 发布DefaultRetryPolicy
pubsub - 订阅DefaultComponentInboundRetryPolicy
statestoreDefaultStatestoreComponentOutboundRetryPolicy
actorstorefastRetries
EventActorretryForever
SummaryActorDefaultActorRetryPolicy

后续步骤

了解如何覆盖默认重试策略。

相关链接

尝试其中一个弹性快速入门:

3 - 目标

将弹性策略应用于目标,包括应用、组件和 actor

目标

命名策略应用于目标。Dapr 支持三种目标类型,这些类型适用于所有 Dapr 构建块 API:

  • apps
  • components
  • actors

应用

使用 apps 目标,您可以将 retrytimeoutcircuitBreaker 策略应用于 Dapr 应用之间的服务调用。在 targets/apps 下,策略应用于每个目标服务的 app-id。当边车之间的通信发生故障时,会触发这些策略,如下图所示。

Dapr 提供了内置服务调用重试,因此任何应用的 retry 策略都是额外的。

Diagram showing service invocation resiliency

针对 app-id 为 “appB” 的目标应用应用策略的示例:

specs:
  targets:
    apps:
      appB: # 目标服务的 app-id
        timeout: general
        retry: general
        circuitBreaker: general

组件

使用 components 目标,您可以将 retrytimeoutcircuitBreaker 策略应用于组件操作。

策略可以应用于 outbound 操作(对 Dapr 边车的调用)和/或 inbound(边车调用您的应用)。

出站

outbound 操作是从边车到组件的调用,例如:

  • 持久化或检索状态。
  • 在发布订阅组件上发布消息。
  • 调用输出绑定。

某些组件可能具有内置的重试功能,并按组件配置。

Diagram showing service invocation resiliency
spec:
  targets:
    components:
      myStateStore:
        outbound:
          retry: retryForever
          circuitBreaker: simpleCB
入站

inbound 操作是从边车到您应用的调用,例如:

  • 发布订阅订阅在传递消息时。
  • 输入绑定。

某些组件可能具有内置的重试功能,并按组件配置。

Diagram showing service invocation resiliency
spec:
  targets:
    components:
      myInputBinding:
        inbound: 
          timeout: general
          retry: general
          circuitBreaker: general
发布订阅

在发布订阅 target/component 中,您可以同时指定 inboundoutbound 操作。

Diagram showing service invocation resiliency
spec:
  targets:
    components:
      myPubsub:
        outbound:
          retry: pubsubRetry
          circuitBreaker: pubsubCB
        inbound: # inbound 仅适用于从边车到应用的传递
          timeout: general
          retry: general
          circuitBreaker: general

Actor

使用 actors 目标,您可以将 retrytimeoutcircuitBreaker 策略应用于 actor 操作。

当为 actors 目标使用 circuitBreaker 策略时,您可以使用 circuitBreakerScope 指定熔断状态应如何确定范围:

  • id:单个 actor ID
  • type:给定 actor 类型的所有 actor
  • both:以上两者

您还可以使用 circuitBreakerCacheSize 属性为内存中保留的熔断器数量指定缓存大小,提供一个整数值,例如 5000

示例

spec:
  targets:
    actors:
      myActorType:
        timeout: general
        retry: general
        circuitBreaker: general
        circuitBreakerScope: both
        circuitBreakerCacheSize: 5000

后续步骤

尝试其中一个弹性快速入门:

4 - Health checks

如何为 Dapr 边车和应用程序设置健康检查

4.1 - App health checks

对应用的健康状态变化做出反应

应用健康检查功能允许探测应用程序的健康状况并对状态变化做出反应。

应用程序可能因各种原因无响应。例如,您的应用程序:

  • 可能过于忙碌而无法接受新工作;
  • 可能已崩溃;或
  • 可能处于死锁状态。

有时这种情况是暂时的,例如:

  • 如果应用只是忙碌,最终会恢复接受新工作
  • 如果应用程序因某种原因正在重启,正处于其初始化阶段

应用健康检查默认禁用。启用应用健康检查后,Dapr 运行时(sidecar)会通过 HTTP 或 gRPC 调用定期探测您的应用程序。当它检测到应用程序健康失败时,Dapr 会停止代表应用程序接受新工作,通过以下方式:

  • 取消订阅所有发布订阅订阅
  • 停止所有输入绑定
  • 短路所有服务调用请求,这些请求在 Dapr 运行时终止,不会转发到应用程序
  • 注销 Dapr Actor 类型,从而在有其他副本可用时导致 Actor 实例迁移到其他副本

这些更改是临时的,一旦 Dapr 检测到应用程序再次响应,就会恢复正常操作。

Diagram showing the app health feature. Running Dapr with app health enabled causes Dapr to periodically probe the app for its health.

应用健康检查 vs 平台级健康检查

Dapr 中的应用健康检查旨在与平台级健康检查(例如在 Kubernetes 上运行时的 liveness probes)互补,而不是替代它们。

平台级健康检查(或 liveness probes)通常确保应用程序正在运行,并在失败时导致平台重启应用程序。

与平台级健康检查不同,Dapr 的应用健康检查专注于暂停向当前无法接受但预期最终能够恢复接受工作的应用程序传递工作。目标包括:

  • 不会给已经超载的应用程序带来更多负载。
  • 通过在 Dapr 知道应用程序无法处理消息时,不从队列、绑定或发布订阅代理获取消息,来做到"礼貌"的行为。

在这方面,Dapr 的应用健康检查更"温和",等待应用程序能够处理工作,而不是以"强硬"的方式终止正在运行的进程。

配置应用健康检查

应用健康检查默认禁用,但可以通过以下任一方式启用:

  • --enable-app-health-check CLI 标志;或
  • 在 Kubernetes 上运行时的 dapr.io/enable-app-health-check: true 注解。

添加此标志是使用默认选项启用应用健康检查的必要且充分条件。

完整的选项列表在下表中列出:

CLI 标志Kubernetes 部署注解描述默认值
--enable-app-health-checkdapr.io/enable-app-health-check启用健康检查的布尔值已禁用
--app-health-check-pathdapr.io/app-health-check-path当应用通道为 HTTP 时,Dapr 为健康探测调用的路径(如果应用通道使用 gRPC,则忽略此值)/healthz
--app-health-probe-intervaldapr.io/app-health-probe-interval每次健康探测之间的5
--app-health-probe-timeoutdapr.io/app-health-probe-timeout健康探测请求的超时时间(毫秒500
--app-health-thresholddapr.io/app-health-threshold在应用被认为不健康之前的连续失败最大次数3

有关所有选项及如何启用它们,请参阅 完整的 Dapr 参数和注解参考

此外,应用健康检查受应用通道使用的协议影响,该协议通过以下标志或注解配置:

CLI 标志Kubernetes 部署注解描述默认值
--app-protocoldapr.io/app-protocol应用通道使用的协议。支持的值为 httpgrpchttpsgrpcsh2c(HTTP/2 Cleartext)。http

健康检查路径

HTTP

当为 app-protocol 使用 HTTP(包括 httphttpsh2c)时,Dapr 通过向 app-health-check-path 中指定的路径(默认为 /health)进行 HTTP 调用来执行健康探测。

为了使您的应用被认为健康,响应必须具有 200-299 范围内的 HTTP 状态码。任何其他状态码都被视为失败。Dapr 只关心响应的状态码,并忽略任何响应头或响应体。

gRPC

当为应用通道使用 gRPC(app-protocol 设置为 grpcgrpcs)时,Dapr 会调用应用程序中的 /dapr.proto.runtime.v1.AppCallbackHealthCheck/HealthCheck 方法。最有可能的是,您将使用 Dapr SDK 来实现此方法的处理程序。

在响应健康探测请求时,您的应用程序可以决定执行额外的内部健康检查,以确定它是否准备好处理来自 Dapr 运行时的工作。但是,这不是必需的;这是一个取决于您的应用程序需求的选择。

间隔、超时和阈值

间隔

默认情况下,启用应用健康检查后,Dapr 每 5 秒探测一次您的应用程序。您可以使用 app-health-probe-interval 配置间隔(以秒为单位)。无论您的应用程序是否健康,这些探测都会定期进行。

超时

当 Dapr 运行时(sidecar)最初启动时,Dapr 会等待成功的健康探测后才认为应用健康。这意味着在第一次健康检查完成并成功之前,不会为您的应用程序启用发布订阅订阅、输入绑定和服务调用请求。

如果应用程序在 app-health-probe-timeout 中配置的超时时间内发送成功响应(如上所述),则健康探测请求被视为成功。默认值为 500,对应 500 毫秒(半秒)。

阈值

在 Dapr 认为应用进入不健康状态之前,它会等待 app-health-threshold 次连续失败,其默认值为 3。此默认值意味着您的应用程序必须连续 3 次健康探测失败才会被认为不健康。

如果将阈值设置为 1,任何失败都会导致 Dapr 假设您的应用不健康并停止向其传递工作。

大于 1 的阈值可以帮助排除由于外部情况导致的暂时性失败。适合您的应用程序的值取决于您的需求。

阈值仅适用于失败。单个成功响应足以让 Dapr 认为您的应用健康并恢复正常操作。

示例

使用 CLI 标志和 dapr run 命令来启用应用健康检查:

dapr run \
  --app-id my-app \
  --app-port 7001 \
  --app-protocol http \
  --enable-app-health-check \
  --app-health-check-path=/healthz \
  --app-health-probe-interval 3 \
  --app-health-probe-timeout 200 \
  --app-health-threshold 2 \
  -- \
    <command to execute>

要在 Kubernetes 中启用应用健康检查,请将相关注解添加到您的 Deployment 中:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  template:
    metadata:
      labels:
        app: my-app
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "my-app"
        dapr.io/app-port: "7001"
        dapr.io/app-protocol: "http"
        dapr.io/enable-app-health-check: "true"
        dapr.io/app-health-check-path: "/healthz"
        dapr.io/app-health-probe-interval: "3"
        dapr.io/app-health-probe-timeout: "200"
        dapr.io/app-health-threshold: "2"

演示

观看此视频以了解使用应用健康检查的概述

4.2 - 边车健康

Dapr 边车健康检查

Dapr 提供了一种使用 HTTP /healthz 端点来确定其健康状况的方法。通过该端点,daprd 进程或边车可以:

  • 探测其整体健康状况
  • 从基础设施平台探测 Dapr 边车就绪状态
  • 在 Kubernetes 中确定就绪状态和存活状态

在本指南中,您将了解 Dapr /healthz 端点如何与应用程序托管平台(例如 Kubernetes)的健康探针以及 Dapr SDK 集成。

下图展示了 Dapr 边车启动时的步骤、healthz 端点以及应用程序通道何时初始化。

Diagram of Dapr checking oubound health connections.

出站健康端点

如上图红色边界线所示,v1.0/healthz/ 端点用于等待以下条件:

  • 所有组件已初始化;
  • Dapr HTTP 端口可用;以及
  • 应用程序通道已初始化。

这用于检查 Dapr 边车的完整初始化及其健康状况。

设置 DAPR_HEALTH_TIMEOUT 环境变量可以让您控制健康超时,例如,这在具有更高延迟的不同环境中可能很重要。

另一方面,如上图绿色边界线所示,当满足以下条件时,v1.0/healthz/outbound 端点会成功返回:

  • 所有组件已初始化;
  • Dapr HTTP 端口可用;
  • 应用程序通道尚未建立。

在 Dapr SDK 中,waitForSidecar 方法(取决于您使用的 SDK)用于使用 v1.0/healthz/outbound 端点进行此特定检查。使用此行为,Dapr 等待来自 v1.0/healthz/outbound 的成功响应,而不是等待应用程序通道可用(参见:红色边界线)与 v1.0/healthz/ 端点。这种方法使您的应用程序能够在应用程序通道初始化之前对 Dapr 边车 API 执行调用——例如,使用 secrets API 读取机密。

如果您在 SDK 上使用 waitForSidecar 方法,则会执行正确的初始化。否则,您可以在初始化期间调用 v1.0/healthz/outbound 端点,如果成功,您可以调用 Dapr 边车 API。

支持出站健康端点的 SDK

目前,v1.0/healthz/outbound 端点在以下 SDK 中受支持:

健康端点:与 Kubernetes 集成

将 Dapr 部署到 Kubernetes 等托管平台时,Dapr 健康端点会自动为您配置。

Kubernetes 使用就绪探针和存活探针来确定容器的健康状况。

存活探针

kubelet 使用存活探针来了解何时重启容器。例如,存活探针可以检测死锁(正在运行但无法继续推进的应用程序)。在这种情况下重启容器可以帮助使应用程序更加可用,尽管存在错误。

如何在 Kubernetes 中配置存活探针

在 Pod 配置文件中,存活探针添加在容器规范部分,如下所示:

    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 3
      periodSeconds: 3

在上面的示例中,periodSeconds 字段指定 kubelet 应每 3 秒执行一次存活探针。initialDelaySeconds 字段告诉 kubelet 在执行第一次探针之前应等待 3 秒。要执行探针,kubelet 向容器中运行并监听端口 8080 的服务器发送 HTTP GET 请求。如果服务器的 /healthz 路径的处理程序返回成功代码,则 kubelet 认为容器是存活和健康的。如果处理程序返回失败代码,则 kubelet 会终止容器并重新启动它。

200 到 399 之间的任何 HTTP 状态码表示成功;任何其他状态码表示失败。

就绪探针

kubelet 使用就绪探针来了解容器何时准备好开始接受流量。当 Pod 的所有容器都准备好时,该 Pod 被视为就绪。此就绪信号的一个用途是控制哪些 Pod 用作 Kubernetes 服务的后端。当 Pod 未就绪时,它会从 Kubernetes 服务负载均衡器中移除。

如何在 Kubernetes 中配置就绪探针

就绪探针的配置方式与存活探针类似。唯一的区别是您使用 readinessProbe 字段而不是 livenessProbe 字段:

    readinessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 3
      periodSeconds: 3

边车注入器

在与 Kubernetes 集成时,Dapr 边车会注入一个 Kubernetes 探针配置,告诉它使用 Dapr healthz 端点。这是由"边车注入器"系统服务完成的。与 kubelet 的集成如下图所示。

Diagram of Dapr services interacting

如何使用 Kubernetes 配置 Dapr 边车健康端点

如上所述,此配置由边车注入器服务自动完成。本节描述了在存活探针和就绪探针上设置的具体值。

Dapr 在端口 3500 上有其 HTTP 健康端点 /v1.0/healthz。这可以与 Kubernetes 一起用于就绪和存活探针。当注入 Dapr 边车时,就绪探针和存活探针在 Pod 配置文件中使用以下值进行配置:

    livenessProbe:
      httpGet:
        path: v1.0/healthz
        port: 3500
      initialDelaySeconds: 5
      periodSeconds: 10
      timeoutSeconds : 5
      failureThreshold : 3
    readinessProbe:
      httpGet:
        path: v1.0/healthz
        port: 3500
      initialDelaySeconds: 5
      periodSeconds: 10
      timeoutSeconds : 5
      failureThreshold: 3

延迟优雅关闭

Dapr 接受 dapr.io/block-shutdown-duration 注解或 --dapr-block-shutdown-duration CLI 标志,这会将完整的关闭过程延迟指定的持续时间,或直到应用程序报告不健康,以先发生者为准。

在此期间,所有订阅和输入绑定都会关闭。这对于需要在关闭过程中使用 Dapr API 的应用程序很有用。

适用的注解或 CLI 标志包括:

  • --dapr-graceful-shutdown-seconds/dapr.io/graceful-shutdown-seconds
  • --dapr-block-shutdown-duration/dapr.io/block-shutdown-duration
  • --dapr-graceful-shutdown-seconds/dapr.io/graceful-shutdown-seconds
  • --dapr-block-shutdown-duration/dapr.io/block-shutdown-duration

注解和参数指南中了解更多信息以及如何使用它们。

相关链接