1. 概述

上一篇文章中,我们探讨了Hystrix的基本概念以及它如何帮助构建健壮的容错应用。许多现有的Spring应用调用外部系统时,都可以从Hystrix中受益。不幸的是,可能无法重写这些应用以集成Hystrix,但借助于Spring AOP,我们可以以非侵入的方式将Hystrix融入其中。

本篇文章将介绍如何将Hystrix与现有Spring应用集成。

2. 将Hystrix融入Spring应用

2.1. 当前应用

让我们先看看应用中现有的客户端调用者,它调用了我们在上一篇文章中创建的RemoteServiceTestSimulator

@Component("springClient")
public class SpringExistingClient {

    @Value("${remoteservice.timeout}")
    private int remoteServiceDelay;

    public String invokeRemoteServiceWithOutHystrix() throws InterruptedException {
        return new RemoteServiceTestSimulator(remoteServiceDelay).execute();
    }
}

如代码片段所示,invokeRemoteServiceWithOutHystrix方法负责调用RemoteServiceTestSimulator远程服务。当然,实际应用会复杂得多。

2.2. 创建环绕通知

为了演示如何集成Hystrix,我们将使用这个客户端作为示例。为此,我们将定义一个环绕通知,当invokeRemoteService执行时触发

@Around("@annotation(com.baeldung.hystrix.HystrixCircuitBreaker)")
public Object circuitBreakerAround(ProceedingJoinPoint aJoinPoint) {
    return new RemoteServiceCommand(config, aJoinPoint).execute();
}

上述通知设计为一个带有@HystrixCircuitBreaker注解的切点上的环绕通知。

现在来看看HystrixCircuitBreaker注解的定义:*

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface HystrixCircuitBreaker {}

2.3. Hystrix逻辑

接下来,我们看看RemoteServiceCommand。在示例代码中,它被实现为一个静态内部类,以封装Hystrix调用逻辑:

private static class RemoteServiceCommand extends HystrixCommand<String> {

    private ProceedingJoinPoint joinPoint;

    RemoteServiceCommand(Setter config, ProceedingJoinPoint joinPoint) {
        super(config);
        this.joinPoint = joinPoint;
    }

    @Override
    protected String run() throws Exception {
        try {
            return (String) joinPoint.proceed();
        } catch (Throwable th) {
            throw new Exception(th);
        }
    }
}

整个Aspect组件的实现可以在这里看到。

2.4. 使用@HystrixCircuitBreaker注解

一旦定义了Aspect,我们可以在客户端方法上添加@HystrixCircuitBreaker注解,如下所示,Hystrix会在每个带有此注解的方法调用时被触发:

@HystrixCircuitBreaker
public String invokeRemoteServiceWithHystrix() throws InterruptedException{
    return new RemoteServiceTestSimulator(remoteServiceDelay).execute();
}

下面的集成测试将展示Hystrix路由和非Hystrix路由之间的差异。

2.5. 测试集成

为了演示,我们定义了两种方法执行路线,一种带有Hystrix,另一种不带。

public class SpringAndHystrixIntegrationTest {

    @Autowired
    private HystrixController hystrixController;

    @Test(expected = HystrixRuntimeException.class)
    public void givenTimeOutOf15000_whenClientCalledWithHystrix_thenExpectHystrixRuntimeException()
      throws InterruptedException {
        hystrixController.withHystrix();
    }

    @Test
    public void givenTimeOutOf15000_whenClientCalledWithOutHystrix_thenExpectSuccess()
      throws InterruptedException {
        assertThat(hystrixController.withOutHystrix(), equalTo("Success"));
    }
}

当测试执行时,你可以看到没有Hystrix的调用会等待远程服务的整个执行时间,而Hystrix路由会在定义的超时(在我们的例子中是10秒)后短路并抛出HystrixRuntimeException

3. 总结

我们可以为每个想要调用的不同远程服务创建一个单独的Aspect,每个Aspect都有不同的配置。在下一篇文章中,我们将从项目开始时就集成Hystrix。本文的所有代码可以在GitHub仓库中找到。