返回介绍

5.5 使用 Hystrix 实现断路器

发布于 2025-04-22 21:54:07 字数 4912 浏览 0 评论 0 收藏

我们将会看到两大类别的 Hystrix 实现。在第一个类别中,我们将使用 Hystrix 断路器包装许可证服务和组织服务中所有对数据库的调用。然后,我们将使用 Hystrix 包装许可证服务和组织服务之间的内部服务调用。虽然这是两个不同类别的调用,但是 Hystrix 的用法是完全一样的。图 5-4 展示了使用 Hystrix 断路器来包装的远程资源。

图 5-4 Hystrix 位于每个远程资源调用之间并保护客户端。远程资源调用是数据库调用还是基于 REST 的服务调用无关紧要

本章将先展示如何使用同步 Hystrix 断路器从许可数据库中检索许可服务数据,以此开始对 Hystrix 的讨论。许可证服务将通过同步调用来检索数据,但在继续处理之前会等待 SQL 语句完成或断路器超时。

Hystrix 和 Spring Cloud 使用 @HystrixCommand 注解来将 Java 类方法标记为由 Hystrix 断路器进行管理。当 Spring 框架看到 @HystrixCommand 时,它将动态生成一个代理,该代理将包装该方法,并通过专门用于处理远程调用的线程池来管理对该方法的所有调用。

我们将包装 licensing-service/src/main/java/com/thoughtmechanix/licenses/services/License Service.java 中的 LicenseService 类中的 getLicensesByOrg() 方法,如代码清单 5-2 所示。

代码清单 5-2 用断路器包装远程资源调用

// 为了简洁,省略了 import 语句
@HystrixCommand  ⇽---  @HystrixCommand 注解会使用 Hystrix 断路器包装 getLicenseByOrg() 方法
public List<License> getLicensesByOrg(String organizationId){
    return licenseRepository.findByOrganizationId(organizationId);
}

注意

如果读者在源代码库中查看代码清单 5-2 中的代码,会在 @HystrixCommand 注解中看到多个参数,而不是像上述代码清单显示的那样。本章稍后将介绍这些参数。代码清单 5-2 中的代码使用了 @HystrixCommand 注解,其中包含了所有默认值。

这看起来代码并不多,但在这一个注解中却有很多功能。使用 @HystrixCommand 注解,在任何时候调用 getLicensesByOrg() 方法时,Hystrix 断路器都将包装这个调用。每当调用时间超过 1000 ms 时,断路器将中断对 getLicensesByOrg() 方法的调用。

如果数据库正常工作,这个代码示例就显得很无聊。因此,通过让调用时间稍微超过 1 s(每 3 次调用中大约有 1 次),让我们来模拟 getLicensesByOrg() 方法执行慢数据库查询。代码清单 5-3 展示了上述讨论的内容。

代码清单 5-3 对许可证服务数据库的随机超时调用

private void randomlyRunLong(){  ⇽---  randomlyRunLong() 方法提供了 1/3 的概率运行耗时较长的数据库调用
    Random rand = new Random();

    int randomNum = rand.nextInt((3 - 1) + 1) + 1;

    if (randomNum==3) sleep();
}

private void sleep(){
    try {
        Thread.sleep(11000);  ⇽---  休眠 11 000 ms(即 11 s),Hystrix 的默认调用时间是 1 s
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

@HystrixCommand
public List<License> getLicensesByOrg(String organizationId){
    randomlyRunLong();

    return licenseRepository.findByOrganizationId(organizationId);
}

如果访问 http://localhost/v1/organizations/e254f8c-c442-4ebe-a82a- e2fc1d1ff78a/licenses/ 端点的次数足够多,那么应该会看到从许可证服务返回的超时错误消息。图 5-5 展示了这个错误。

图 5-5 当远程调用花费时间过长时,会抛出一个 HystrixRuntimeException 异常

现在,有了 @HystrixCommand 注解,如果查询花费的时间过长,许可证服务将中断其对数据库的调用。如果需要超过 1000 ms 的时间来执行 Hystrix 代码包装的数据库调用,那么服务调用将抛出一个 com.nextflix.hystrix.exception.HystrixRuntimeException 异常。

5.5.1 对组织微服务的调用超时

我们可以使用方法级注解使被标记的调用拥有断路器功能,其优点在于,无论是访问数据库还是调用微服务,它都是相同的注解。

例如,在许可证服务中,我们需要查找与许可证关联的组织的名称。如果要使用断路器来包装对组织服务的调用的话,一个简单的方法就是将 RestTemplate 调用分解到自己的方法,并使用 @HystrixCommand 注解进行标注:

@HystrixCommand
private Organization getOrganization(String organizationId) {
    return organizationRestClient.getOrganization(organizationId);
}

注意

虽然使用 @HystrixCommand 很容易实现,但在使用没有任何配置的默认的 @HystrixCommand 注解时要特别小心。在默认情况下,在指定不带属性的 @HystrixCommand 注解时,这个注解会将所有远程服务调用都放在同一线程池下。这可能会导致应用程序中出现问题。在本章稍后讨论如何实现舱壁模式时,将展示如何将这些远程服务调用隔离到它们自己的线程池中,并配置线程池的行为以相互独立。

5.5.2 定制断路器的超时时间

在与新的开发人员合作使用 Hystrix 进行开发时,我经常遇到的第一个问题是,他们如何定制 Hystrix 中断调用之前的时间。这一点通过将附加的参数传递给 @HystrixCommand 注解可以轻松完成。代码清单 5-4 演示了如何定制 Hystrix 在超时调用之前等待的时间。

代码清单 5-4 定制断路器调用超时

@HystrixCommand(
→  commandProperties = {
        @HystrixProperty(   ⇽---  commandProperties 属性允许开发人员提供附加的属性来定制 Hystrix
            name="execution.isolation.thread.timeoutInMilliseconds",  ⇽---  execution.isolation.thread.timeoutInMilliseconds 用于设置断路器的超时时间(以毫秒为单位)
            →  value="12000")})
public List<License> getLicensesByOrg(String organizationId){
    randomlyRunLong();

    return licenseRepository.findByOrganizationId(organizationId);  
}

Hystrix 允许通过 commandProperties 属性来定制断路器的行为。 commandProperties 属性接受一个 HystrixProperty 对象数组,它可以传入自定义属性来配置 Hystrix 断路器。在代码清单 5-4 中,使用 execution.isolation.thread.timeoutInMilliseconds 属性设置 Hystrix 调用的最大超时时间为 12 s。

现在,如果重新构建并重新运行这个代码示例,则永远都不会出现超时错误,因为人工超时时间为 11 s,而 @HystrixCommand 注解现在配置为 12 s 后才会超时。

服务超时

显然,12 s 的断路器超时只是我用来作为教学的一个例子。在分布式环境中,如果我开始听到开发团队反馈,说远程服务调用上的 1 s 超时时间太少了,因为他们的服务 X 平均需要 5~6 s 的时间,那么我就会经常感到紧张。

这些反馈通常告诉我,被调用的服务存在未解决的性能问题。开发人员应避免在 Hystrix 调用上增加默认超时的诱惑,除非实在无法解决运行缓慢的服务调用。

如果确实遇到一些比其他服务调用需要更长时间的服务调用,务必将这些服务调用隔离到单独的线程池中。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。