开源软件:Hystrix 02

Hystrix 异常处理机制

Posted by Shoukai Huang on August 25, 2020

Hystrix 错误类型

Hystrix 的异常处理中,有5种出错的情况下会被 fallback 所截获,从而触发 fallback,这些情况是:

  • FAILURE:执行失败,抛出异常。
  • TIMEOUT:执行超时。
  • SHORT_CIRCUITED:断路器打开。
  • THREAD_POOL_REJECTED:线程池拒绝。
  • SEMAPHORE_REJECTED:信号量拒绝。

有一种类型的异常是不会触发 fallback 且不会被计数进入熔断的,它是 BAD_REQUEST,会抛出 HystrixBadRequestException,这种异常一般对应的是由非法参数或者一些非系统异常引起的,对于这类异常可以根据响应创建对应的异常进行异常封装或者直接处理。

通常,HystrixCommand 是通过实现 getFallback() 实现失败返回。Hystrix将对所有类型的故障(例如run()故障,超时,线程池或信号灯拒绝以及断路器短路)执行此后备。异常示例,手动抛出异常

public class CommandThatFailsFast extends HystrixCommand<String> {

    private final boolean throwException;

    public CommandThatFailsFast(boolean throwException) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.throwException = throwException;
    }

    @Override
    protected String run() {
        if (throwException) {
            throw new RuntimeException("failure from CommandThatFailsFast");
        } else {
            return "success";
        }
    }

    public static class UnitTest {

        @Test
        public void testSuccess() {
            assertEquals("success", new CommandThatFailsFast(false).execute());
        }

        @Test
        public void testFailure() {
            try {
                new CommandThatFailsFast(true).execute();
                fail("we should have thrown an exception");
            } catch (HystrixRuntimeException e) {
                assertEquals("failure from CommandThatFailsFast", e.getCause().getMessage());
                e.printStackTrace();
            }
        }
    }
}

执行 execute() 方法后,会抛出 HystrixRuntimeException 异常

当 实现 CommandThatFailsFast 实现 getFallback() 方法后,HystrixCommand 捕捉异常后会返回 getFallback() 的结果

public class CommandThatFailsFast extends HystrixCommand<String> {
    // ......
    protected String getFallback() {
        return "fail";
    }
    // ......

feign-hystrix

在 feign 和 hystrix 结合使用时,会实现 FallbackFactory 来实现异常返回方法。 HystrixInvocationHandler 使用了 HystrixCommand 封装 http 的访问命令,实现了 getFallback() 方法来封装异常并调用 FallbackFactory 方法。

代码定义:

HystrixCommand<Object> hystrixCommand =
new HystrixCommand<Object>(setterMethodMap.get(method)) {
  @Override
  protected Object run() throws Exception {
      // ......
  }

  @Override
  protected Object getFallback() {
    if (fallbackFactory == null) {
      return super.getFallback();
    }
    try {
      Object fallback = fallbackFactory.create(getExecutionException());
      Object result = fallbackMethodMap.get(method).invoke(fallback, args);
      if (isReturnsHystrixCommand(method)) {
        return ((HystrixCommand) result).execute();
      } else if (isReturnsObservable(method)) {
        // Create a cold Observable
        return ((Observable) result).toBlocking().first();
      } else if (isReturnsSingle(method)) {
        // Create a cold Observable as a Single
        return ((Single) result).toObservable().toBlocking().first();
      } else if (isReturnsCompletable(method)) {
        ((Completable) result).await();
        return null;
      } else if (isReturnsCompletableFuture(method)) {
        return ((Future) result).get();
      } else {
        return result;
      }
    // ......
  }
};

使用测试用例进行测试:

@Test
public void fallbackFactory_example_lambda() {
  server.enqueue(new MockResponse().setResponseCode(500));
  server.enqueue(new MockResponse().setResponseCode(404));

  TestInterface api = target(cause -> () -> {
    assertThat(cause).isInstanceOf(FeignException.class);
    return ((FeignException) cause).status() == 500 ? "foo" : "bar";
  });

  assertThat(api.invoke()).isEqualTo("foo");
  assertThat(api.invoke()).isEqualTo("bar");
}

其中封装的 http 异常能够通过 FallbackFactory 返回给业务实现类

参考