12、Helidon 容错与健康检查:构建健壮应用的关键

Helidon容错与健康检查构建健壮应用

Helidon 容错与健康检查:构建健壮应用的关键

在现代应用开发中,处理各种错误和故障是确保应用程序健壮性和可靠性的关键。Helidon 提供了一系列强大的工具来帮助开发者应对这些挑战,本文将深入探讨 Helidon 的容错和健康检查功能。

1. Helidon 容错概述

Helidon 容错是 MicroProfile 容错规范的兼容实现,是 Helidon 应用程序可观测性特性之一。它通过一系列注解来提高应用程序的健壮性,方便处理现实世界应用中可能出现的错误条件或故障,如服务重启、网络延迟和临时基础设施不稳定等。

1.1 Helidon 容错注解

Helidon 容错定义了以下注解:
| 注解 | 简短描述 |
| — | — |
| @Retry | 在方法执行失败时重试 |
| @Timeout | 定义方法执行时间的上限 |
| @CircuitBreaker | 定义策略以避免重复执行可能失败的逻辑 |
| @Bulkhead | 定义策略以限制应用程序逻辑允许的并发执行数量 |
| @Fallback | 建立在调用失败时执行的处理程序 |
| @Asynchronous | 异步执行调用而不阻塞调用线程 |

下面我们将详细介绍这些注解及其参数。

1.2 @Retry 注解

@Retry 注解用于在方法执行失败时重试。可以使用注解属性控制重试次数、重试之间的延迟以及触发重试或中止的异常类型。

@Retry(
    maxRetries=3,
    delay=0,
    delayUnit=ChronoUnit.MILLIS,
    maxDuration=180000,
    durationUnit=ChronoUnit.MILLIS,
    jitter=200,
    jitterDelayUnit=ChronoUnit.MILLIS,
    retryOn={Exception.class},
    abortOn={}
)

@Retry 注解的参数说明如下:
| 参数 | 描述 | 默认值 |
| — | — | — |
| int maxRetries() | 最大重试次数,-1 表示永远重试,值必须大于或等于 -1 | 3 |
| long delay() | 重试之间的延迟,值必须大于或等于 0 | 0 |
| ChronoUnit delayUnit() | delay() 参数的指定延迟单位 | ChronoUnit.MILLIS |
| long maxDuration() | 执行重试的最大持续时间,必须大于延迟持续时间(如果设置),0 表示未设置最大持续时间 | 180000 ms |
| ChronoUnit durationUnit() | maxDuration() 参数的持续时间单位 | ChronoUnit.MILLIS |
| long jitter() | 设置抖动以随机改变重试延迟,值必须大于或等于 0,0 表示未设置抖动,有效延迟计算为 [delay - jitter, delay + jitter],且应始终大于或等于 0,负延迟将设置为 0 | 200 ms |
| ChronoUnit jitterDelayUnit() | jitter() 参数的延迟单位 | ChronoUnit.MILLIS |
| Class<? extends Throwable>[] retryOn() | 应触发重试的异常类型列表 | {Exception.class} |
| Class<? extends Throwable>[] abortOn() | 不应触发重试的异常类型列表 | {} |

1.3 @Timeout 注解

@Timeout 注解定义方法执行时间的上限。

@Timeout(
    value=1000,
    unit=ChronoUnit.MILLIS
)

@Timeout 注解的参数说明如下:
| 参数 | 描述 | 默认值 |
| — | — | — |
| long value() | 超时值,必须大于或等于 0,0 表示未配置超时,否则将抛出 FaultToleranceDefinitionException | 1000 ms |
| ChronoUnit unit() | 超时单位 | ChronoUnit.MILLIS |

1.4 @CircuitBreaker 注解

@CircuitBreaker 注解定义策略以避免重复执行可能失败的逻辑。断路器有闭合、打开或半开三种状态。

@CircuitBreaker(
    failOn={Throwable.class},
    skipOn={},
    delay=5000,
    delayUnit=ChronoUnit.MILLIS,
    requestVolumeThreshold=20,
    failureRatio=.50,
    successThreshold=1
)

@CircuitBreaker 注解的参数说明如下:
| 参数 | 描述 | 默认值 |
| — | — | — |
| Class<? extends Throwable>[] failOn() | 应视为失败的异常类型列表 | {Throwable.class} |
| Class<? extends Throwable>[] skipOn() | 不应视为失败的异常类型列表,此列表优先于 failOn() 参数中的类型 | {} |
| long delay() | 打开的电路过渡到半开状态后的延迟,值必须大于或等于 0,0 表示无延迟 | 5000 ms |
| long delayUnit() | 打开的电路过渡到半开状态后的延迟单位 | ChronoUnit.MILLIS |
| int requestVolumeThreshold() | 滚动窗口中的连续请求数,若失败次数超过 failureRatio() 返回的值,断路器将跳闸 | 20 |
| double failureRatio() | 滚动窗口内导致电路跳闸到打开状态的失败比率,值必须在 0 到 1 之间(包括 0 和 1) | 0.5 |
| int successThreshold() | 半开电路再次闭合前的成功执行次数,半开电路在无失败执行 successThreshold() 次后将闭合,若在半开状态下发生失败,电路将立即再次打开 | 1 |

1.5 @Bulkhead 注解

@Bulkhead 注解定义策略以限制应用程序逻辑允许的并发执行数量。达到定义的限制后,使用队列来停放等待执行的任务。队列仅在调用使用 @Asynchronous 注解时才会激活。

@Bulkhead(
    value=10,
    waitingTaskQueue=10
)

@Bulkhead 注解的参数说明如下:
| 参数 | 描述 | 默认值 |
| — | — | — |
| int value() | 指定对实例的最大并发调用数,值必须大于 0,否则将抛出 FaultToleranceDefinitionException | 10 |
| int waitingTaskQueue() | 指定等待任务队列,此设置仅在使用 @Asynchronous 注解进行异步调用时生效,值必须大于 0,否则将抛出 FaultToleranceDefinitionException | 10 |

1.6 @Fallback 注解

@Fallback 注解建立在调用失败时执行的处理程序。处理程序可以是实现 FallbackHandler 接口的类,也可以是同一类中的简单方法。

@Fallback(
    value=DEFAULT.class,
    fallbackMethod="",
    applyOn={Throwable.class},
    skipOn={}
)

@Fallback 注解的参数说明如下:
| 参数 | 描述 | 默认值 |
| — | — | — |
| Class<? extends FallbackHandler<?>> value() | 指定要使用的回退类,返回 Fallback 接口的新实例,但该实例不受管理,Fallback 接口的类型参数 必须可分配给注解方法的返回类型,否则将抛出 FaultToleranceDefinitionException | DEFAULT.class |
| String fallbackMethod() | 指定要回退的方法名称,此方法属于要回退的方法所在的同一类,方法必须具有与注解方法完全相同的参数,方法返回类型必须可分配给回退方法的返回类型,否则将抛出 FaultToleranceDefinitionException | “” |
| Class<? extends Throwable>[] applyOn() | 应触发回退的异常类型列表 | {Throwable.class} |
| Class<? extends Throwable>[] skipOn() | 不应触发回退的异常类型列表,此列表优先于 applyOn() 参数中的类型 | {} |

1.7 @Asynchronous 注解

@Asynchronous 注解用于异步执行调用而不阻塞调用线程。注解的方法必须返回 Future 或 CompletionStage 类型。此注解通常用于避免在 I/O 或长时间运行的计算上阻塞调用线程,该注解没有参数。

2. 构建示例应用

为了演示 Helidon 容错的实际应用,我们将构建一个小的示例应用,模拟故障并展示如何解决它们。该应用由三个主要类组成:Message、SimpleGreetResource 和 FaultToleranceResource。

2.1 依赖

首先,我们需要添加 Maven 或 Gradle 依赖。

如果使用 Maven,添加以下依赖:

<dependency>
    <groupId>io.helidon.microprofile</groupId>
    <artifactId>helidon-microprofile-fault-tolerance</artifactId>
    <version>4.1.0</version>
</dependency>

如果使用 Gradle,添加以下依赖:

implementation group: 'io.helidon.microprofile', name: 'helidon-microprofile-fault-tolerance', version: '4.1.0'
2.2 Fallback 示例

在 FaultToleranceResource 类中,fallbackHandler() 方法定义了一个端点和 @Fallback 注解中分配的回退处理程序。

@Fallback(fallbackMethod = "fallbackMethod") 
@Path("/fallback/{success}") 
@GET
public Response fallbackHandler(@PathParam("success") String success) { 
    if (!Boolean.parseBoolean(success)) { 
        terminate(); 
    }
    return reactive(); 
}

private Response fallbackMethod(String success) {
    return Response.ok("Fallback endpoint reached").build();
}

private void terminate() {
    throw new RuntimeException("failure");
}

private Response reactive() {
    return Response.ok(blocking()).build();
}

private String blocking() {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException ignored) {
    }
    return "Blocked for 5 seconds";
}
2.3 Retry 示例

retryHandler() 方法定义了一个端点和 @Retry 注解中分配的最大重试次数。

private static int retry; 
@Retry(maxRetries = 2) 
@Path("/retry") 
@GET
public Response retryHandler() { 
    if (++retry < 2) { 
        terminate();
    }
    String response = String.format("Number of failures: %s", retry); 
    retry = 0;
    return Response.ok(response).build();
}
2.4 构建和执行应用

从命令行使用 Maven 构建和打包应用程序为 JAR 文件:

~ » mvn clean package

然后使用 java 命令调用 JAR 文件:

~ » java -jar target/faultolerance.jar
2.5 测试应用

使用 curl 命令测试 @Fallback 注解:

~ » curl http://localhost:8080/ft/fallback/true
Blocked for 5 seconds

~ » curl http://localhost:8080/ft/fallback/false
Fallback endpoint reached

测试 @Retry 注解:

~ » curl http://localhost:8080/ft/retry
Number of failures: 2
3. Helidon 健康检查

Helidon 健康检查是 MicroProfile 健康检查规范的兼容实现,是 Helidon 应用程序可观测性特性之一。默认情况下,符合 MicroProfile 的应用程序会自动生成提供默认健康检查的健康相关端点,你可以使用 Helidon 健康检查中可用的注解添加自己的自定义健康检查。

3.1 Helidon 健康检查类型和 REST 端点

Web 应用程序可以使用以下 REST 端点检索 Helidon 支持的健康检查:
| 类型 | 描述 | 端点 |
| — | — | — |
| Liveness | 报告运行时环境在该时间点是否足以使微服务执行其任务。如果微服务实例报告 Liveness 检查为 DOWN,则不应再报告为 UP,需要停止并替换实例 | /health/live |
| Readiness | 报告微服务在该时间点是否能够执行其任务。报告 DOWN 的服务无法执行其任务,但可能在未来某个时间点无需重启即可执行 | /health/ready |
| Startup | 报告微服务是否已启动到 Liveness 和 Readiness 检查相关的程度。报告 Startup 检查为 DOWN 的服务很可能仍在初始化,假设能够成功启动,很快将报告为 UP | /health/started |

这些端点的响应根据执行健康检查的结果报告 HTTP 200 (OK)、HTTP 204 (No Content) 或 HTTP 503 (Service Unavailable)。HTTP GET 响应包括 JSON 内容,显示服务器在收到请求后执行的所有健康检查的详细结果,HTTP HEAD 请求仅返回状态,无有效负载。

支持三种健康检查类型的注解如下:
| 注解 | 简短描述 |
| — | — |
| @Liveness | 表示该类是 Liveness 健康检查程序 |
| @Readiness | 表示该类是 Readiness 健康检查程序 |
| @Startup | 表示该类是 Startup 健康检查程序 |

通过使用 Helidon 的容错和健康检查功能,开发者可以构建更健壮、可靠的应用程序,更好地应对各种错误和故障。希望本文能帮助你更好地理解和应用 Helidon 的这些强大特性。

Helidon 容错与健康检查:构建健壮应用的关键

4. 健康检查响应分析

不同类型的健康检查端点返回的响应状态码及内容具有特定的含义,下面详细分析:

  • HTTP 200 (OK) :表示所有健康检查都通过,微服务处于正常工作状态。对于 HTTP GET 请求,响应中会包含 JSON 格式的详细检查结果,方便开发者了解各个检查项的具体情况。
  • HTTP 204 (No Content) :意味着没有配置相应的健康检查,或者检查结果为空。这种情况通常出现在没有为特定类型的健康检查添加自定义检查逻辑时。
  • HTTP 503 (Service Unavailable) :表明至少有一个健康检查失败,微服务可能无法正常工作。此时,HTTP GET 响应的 JSON 内容会指出哪些检查项失败,帮助定位问题。

下面是一个简单的 mermaid 流程图,展示了健康检查的响应逻辑:

graph LR
    A[发起健康检查请求] --> B{检查结果}
    B -->|全部通过| C[HTTP 200 (OK)]
    B -->|无检查项或结果为空| D[HTTP 204 (No Content)]
    B -->|至少一个失败| E[HTTP 503 (Service Unavailable)]
    C --> F[返回 JSON 详细结果]
    E --> G[返回含失败项的 JSON 结果]
5. 自定义健康检查实现

除了使用默认的健康检查,开发者还可以根据应用的特定需求添加自定义健康检查。以下是实现自定义健康检查的步骤:

  1. 创建健康检查类 :创建一个类,并使用相应的健康检查注解(@Liveness、@Readiness 或 @Startup)进行标注。
  2. 实现健康检查逻辑 :在类中实现 HealthCheck 接口的 call() 方法,该方法返回一个 HealthCheckResponse 对象,表示检查结果。
  3. 注册健康检查 :确保自定义的健康检查类被正确注册到应用中,以便在访问健康检查端点时能够执行。

以下是一个自定义 Liveness 健康检查的示例代码:

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;

import javax.enterprise.context.ApplicationScoped;

@Liveness
@ApplicationScoped
public class CustomLivenessCheck implements HealthCheck {

    @Override
    public HealthCheckResponse call() {
        // 这里实现自定义的检查逻辑
        boolean isHealthy = checkCustomCondition();
        if (isHealthy) {
            return HealthCheckResponse.up("Custom Liveness Check");
        } else {
            return HealthCheckResponse.down("Custom Liveness Check");
        }
    }

    private boolean checkCustomCondition() {
        // 模拟自定义检查条件
        return Math.random() > 0.5;
    }
}
6. 综合应用:容错与健康检查结合

在实际应用中,将 Helidon 的容错和健康检查功能结合使用,可以构建出更加健壮、可靠的系统。以下是一个综合应用的示例流程:

  1. 服务调用 :应用程序调用某个服务方法,该方法可能会因为网络延迟、服务故障等原因失败。
  2. 容错处理 :使用 Helidon 容错注解(如 @Retry、@CircuitBreaker 等)对服务方法进行保护。例如,当方法调用失败时,@Retry 注解会自动重试一定次数;@CircuitBreaker 注解可以避免对可能失败的逻辑进行重复调用。
  3. 健康检查 :定期或在关键操作前后,通过健康检查端点(/health/live、/health/ready、/health/started)检查服务的健康状态。如果健康检查失败,及时采取相应的措施,如重启服务、切换到备用服务等。

下面是一个 mermaid 流程图,展示了容错与健康检查结合的应用流程:

graph LR
    A[应用调用服务方法] --> B{方法调用成功?}
    B -->|是| C[继续后续操作]
    B -->|否| D{是否在重试次数内?}
    D -->|是| E[使用 @Retry 重试]
    E --> A
    D -->|否| F[触发 @CircuitBreaker]
    F --> G[定期进行健康检查]
    G --> H{健康检查通过?}
    H -->|是| I[恢复服务调用]
    H -->|否| J[采取恢复措施]
    J --> G
7. 总结

Helidon 的容错和健康检查功能为开发者提供了强大的工具,用于构建健壮、可靠的应用程序。通过使用一系列的容错注解,可以有效地处理应用程序中可能出现的各种故障;而健康检查功能则可以实时监控服务的健康状态,及时发现并解决潜在的问题。

在实际开发中,建议开发者根据应用的具体需求合理使用这些功能。例如,对于关键业务逻辑,使用 @Retry 和 @CircuitBreaker 注解进行保护;同时,添加自定义的健康检查,确保服务的健康状态能够得到准确的评估。通过将容错和健康检查功能有机结合,可以大大提高应用程序的稳定性和可用性,为用户提供更好的服务体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值