首页 > 解决方案 > Hystrix 没有短路

问题描述

我正在尝试以这种方式设置 HystrixCommand 的行为:

public abstract class AbstractCircuitBreakerCommand<E> extends HystrixCommand<E> {

    protected AbstractCircuitBreakerCommand(final String groupKey, final String commandKey) {

        this(groupKey, commandKey, TimeUnit.SECONDS, 10, TimeUnit.SECONDS, 3, TimeUnit.SECONDS, 10, 1);
    }

    protected AbstractCircuitBreakerCommand(
            final String groupKey, final String commandKey,
            final TimeUnit metricsWindowTimeUnit, final int metricsWindowTime,
            final TimeUnit timeoutTimeUnit, final int timeoutTime,
            final TimeUnit windowTimeUnit, final int windowTime,
            final int threshold) {

        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
                .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                        .withMetricsRollingStatisticalWindowInMilliseconds((int) metricsWindowTimeUnit.toMillis(metricsWindowTime))
                        .withExecutionTimeoutEnabled(true)
                        .withExecutionTimeoutInMilliseconds((int) timeoutTimeUnit.toMillis(timeoutTime))
                        .withCircuitBreakerEnabled(true)
                        .withCircuitBreakerRequestVolumeThreshold(threshold)
                        .withCircuitBreakerErrorThresholdPercentage(0)
                        .withCircuitBreakerSleepWindowInMilliseconds((int) windowTimeUnit.toMillis(windowTime))
                        .withFallbackEnabled(true)));
    }

}

我期望的是在接下来的 10 秒(sleepwindowinmilis)仅发生一个异常(或超时)后,将传入的命令短路。

为了测试它,我进行了下一个单元测试。

public class AbstractCircuitBreakerCommandTest {

    private final static int ERROR_VALUE = Integer.MIN_VALUE;

    private final static String GROUP_KEY = "GROUP_KEY";
    private final static String COMMAND_KEY = "COMMAND_KEY";

    @Test
    public void testSimpleExecution() {

        final int expectedValue = 1;

        final SimpleCircuitBreakerCommandToTest circuitBreakerCommand =
                new SimpleCircuitBreakerCommandToTest(GROUP_KEY, COMMAND_KEY, value -> expectedValue);

        Assert.assertEquals(expectedValue, (int) circuitBreakerCommand.execute());
        Assert.assertTrue(circuitBreakerCommand.isExecutionComplete());
        Assert.assertTrue(circuitBreakerCommand.isSuccessfulExecution());
    }

    @Test
    public void testSimpleUnsuccessfulExecutionBecauseException() {

        final SimpleCircuitBreakerCommandToTest circuitBreakerCommand =
                new SimpleCircuitBreakerCommandToTest(GROUP_KEY, COMMAND_KEY, value -> {

                    throw new Exception("Test");
                });

        Assert.assertEquals(ERROR_VALUE, (int) circuitBreakerCommand.execute());
        Assert.assertTrue(circuitBreakerCommand.isExecutionComplete());
        Assert.assertTrue(circuitBreakerCommand.isFailedExecution());
        Assert.assertTrue(circuitBreakerCommand.isCircuitBreakerOpen());
    }

    @Test
    public void testSimpleUnsuccessfulExecutionBecauseTimeout() {

        final SimpleCircuitBreakerCommandToTest circuitBreakerCommand =
                new SimpleCircuitBreakerCommandToTest(GROUP_KEY, COMMAND_KEY, value -> {

                    SleepHelper.sleep(TimeUnit.SECONDS, 4); // The default value is -> 3 seconds.

                    return 1;
                });

        Assert.assertEquals(ERROR_VALUE, (int) circuitBreakerCommand.execute());
        Assert.assertTrue(circuitBreakerCommand.isExecutionComplete());
        Assert.assertTrue(circuitBreakerCommand.isFailedExecution());
        Assert.assertTrue(circuitBreakerCommand.isCircuitBreakerOpen());
    }

    private static abstract class AbstractCircuitBreakerCommandToTest extends AbstractCircuitBreakerCommand<Integer> {

        private final Integer value;
        private final TestAction testAction;

        protected AbstractCircuitBreakerCommandToTest(
                final String groupKey, final String commandKey,
                final TestAction testAction) {

            this(groupKey, commandKey, 1, testAction);
        }

        protected AbstractCircuitBreakerCommandToTest(
                final String groupKey, final String commandKey,
                final Integer value, final TestAction testAction) {

            super(groupKey, commandKey);

            this.value = value;
            this.testAction = testAction;
        }

        @Override
        protected Integer run() throws Exception {

            return this.testAction.run(this.value);
        }

        @Override
        protected Integer getFallback() {

            return ERROR_VALUE;
        }

        @FunctionalInterface
        interface TestAction {

            Integer run(final Integer integer) throws Exception;

        }

    }

    private static class SimpleCircuitBreakerCommandToTest extends AbstractCircuitBreakerCommandToTest {

        protected SimpleCircuitBreakerCommandToTest(
                final String groupKey, final String commandKey,
                final TestAction testAction) {

            super(groupKey, commandKey, testAction);
        }

    }

} 

但是 isCircuitBreakerOpen() 方法在 testSimpleUnsuccessfulExecutionBecauseException 和 testSimpleUnsuccessfulExecutionBecauseTimeout 测试中返回 false。

有人可以指导我正确的方式吗?谢谢你。

标签: javahystrix

解决方案


这些测试中有一些错误,这是修复的版本。

public class AbstractCircuitBreakerCommandTest {

    private final static int WAIT_WORK_DONE_IN_MS = 800;
    private final static int ERROR_VALUE = Integer.MIN_VALUE;

    private final static String GROUP_KEY = "GROUP_KEY";
    private final static String COMMAND_KEY = "COMMAND_KEY";

    @Test
    public void testSimpleExecution() {

        final int expectedValue = 1;

        final HystrixCommand circuitBreakerCommand =
                new SimpleCircuitBreakerCommandToTest(GROUP_KEY + "_0", COMMAND_KEY + "_0", value -> value);

        Assert.assertEquals(expectedValue, (int) circuitBreakerCommand.execute());
        Assert.assertTrue(circuitBreakerCommand.isExecutionComplete());
        Assert.assertTrue(circuitBreakerCommand.isSuccessfulExecution());
    }

    @Test
    public void testSimpleUnsuccessfulExecutionBecauseException() {

        final HystrixCommand circuitBreakerCommand =
                new SimpleCircuitBreakerCommandToTest(GROUP_KEY + "_1", COMMAND_KEY + "_1", value -> {

                    throw new Exception("Test");
                });

        Assert.assertEquals(ERROR_VALUE, (int) circuitBreakerCommand.execute());

        SleepHelper.sleep(TimeUnit.MILLISECONDS, WAIT_WORK_DONE_IN_MS);

        Assert.assertTrue(circuitBreakerCommand.isExecutionComplete());
        Assert.assertTrue(circuitBreakerCommand.isFailedExecution());
        Assert.assertTrue(circuitBreakerCommand.isCircuitBreakerOpen());
    }

    @Test
    public void testSimpleUnsuccessfulExecutionBecauseTimeout() {

        final HystrixCommand circuitBreakerCommand =
                new SimpleCircuitBreakerCommandToTest(GROUP_KEY + "_2", COMMAND_KEY + "_2", value -> {

                    SleepHelper.sleep(TimeUnit.SECONDS, 4); // The default value is -> 3 seconds.

                    return value;
                });

        Assert.assertEquals(ERROR_VALUE, (int) circuitBreakerCommand.execute());

        SleepHelper.sleep(TimeUnit.MILLISECONDS, WAIT_WORK_DONE_IN_MS);

        Assert.assertTrue(circuitBreakerCommand.isExecutionComplete());
        Assert.assertTrue(circuitBreakerCommand.isResponseTimedOut());
        Assert.assertTrue(circuitBreakerCommand.isCircuitBreakerOpen());
    }

    private static abstract class AbstractCircuitBreakerCommandToTest extends AbstractCircuitBreakerCommand<Integer> {

        private final Integer value;
        private final TestAction testAction;

        protected AbstractCircuitBreakerCommandToTest(
                final String groupKey, final String commandKey,
                final TestAction testAction) {

            this(groupKey, commandKey, 1, testAction);
        }

        protected AbstractCircuitBreakerCommandToTest(
                final String groupKey, final String commandKey,
                final Integer value, final TestAction testAction) {

            super(groupKey, commandKey);

            this.value = value;
            this.testAction = testAction;
        }

        @Override
        protected Integer run() throws Exception {

            return this.testAction.run(this.value);
        }

        @Override
        protected Integer getFallback() {

            return ERROR_VALUE;
        }

        @FunctionalInterface
        interface TestAction {

            Integer run(final Integer integer) throws Exception;

        }

    }

    private static class SimpleCircuitBreakerCommandToTest extends AbstractCircuitBreakerCommandToTest {

        protected SimpleCircuitBreakerCommandToTest(
                final String groupKey, final String commandKey,
                final TestAction testAction) {

            super(groupKey, commandKey, testAction);
        }

    }

}

推荐阅读