Тест-драйв Hystrix Конфигурация выключателя

Наше приложение написано в хрупкой манере путем реализации шаблона автоматического выключателя с использованием Hystrix.

Все приложение создается с использованием практики, основанной на тестировании, но застряло в точке, где нам нужно реализовать стратегию автоматического выключателя, настроив ее в методах.

Ниже приведен пример используемой нами конфигурации:

@HystrixCommand(commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "8"),
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "25"),
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")},
        fallbackMethod = "retrieveMapFallback")

Может ли кто-нибудь прокомментировать, есть ли доступная функция или возможность протестировать ее в моем тесте интеграции (который загружает весь WebApplicationContext и, следовательно, знает обо всех конфигурациях, доступных в приложении)?

Или, если это вообще невозможно проверить в контексте моего приложения?

Любые входы будут иметь ценность.

1 ответ

Решение

Вы можете проверить свои Hystrix Конфигурация выключателя.

Например, посмотрите на этот пример приложения с Spring Boot 1.4:

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.stereotype.Component;

@EnableCircuitBreaker
@SpringBootApplication
public class HystrixDemo {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDemo.class, args);
    }

    @Component
    static class MyService {

        static final String COMMAND_KEY = "MyCommandKey";

        private final Outbound outbound;

        MyService(Outbound outbound) {
            this.outbound = outbound;
        }

        @HystrixCommand(
                commandKey = COMMAND_KEY,
                commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2016")
                })
        void process() {
            outbound.call();
        }
    }

    interface Outbound {
        void call();
    }
}

Ваши тесты конфигурации могут выглядеть так:

import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandMetrics;
import com.netflix.hystrix.HystrixCommandProperties;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.assertTrue;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyServiceCircuitBreakerConfigurationTests {

    @Autowired
    private HystrixDemo.MyService myService;

    @MockBean
    private HystrixDemo.Outbound outbound;

    @Before
    public void setup() {
        warmUpCircuitBreaker();
    }

    @Test
    public void shouldHaveCustomTimeout() {
        assertTrue(getCircuitBreakerCommandProperties().executionTimeoutInMilliseconds().get() == 2016);
    }

    private void warmUpCircuitBreaker() {
        myService.process();
    }

    public static HystrixCommandProperties getCircuitBreakerCommandProperties() {
        return HystrixCommandMetrics.getInstance(getCommandKey()).getProperties();
    }

    private static HystrixCommandKey getCommandKey() {
        return HystrixCommandKey.Factory.asKey(HystrixDemo.MyService.COMMAND_KEY);
    }
}

Кроме того, если вы хотите проверить автоматический выключатель, вы можете взглянуть на этот тест:

import com.netflix.config.ConfigurationManager;
import com.netflix.hystrix.Hystrix;
import com.netflix.hystrix.HystrixCircuitBreaker;
import com.netflix.hystrix.HystrixCommandKey;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.willThrow;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyServiceCircuitBreakerTests {

    @Autowired
    private HystrixDemo.MyService myService;

    @MockBean
    private HystrixDemo.Outbound outbound;

    @Before
    public void setup() {
        resetHystrix();
        warmUpCircuitBreaker();
        openCircuitBreakerAfterOneFailingRequest();
    }

    @Test
    public void shouldTripCircuit() throws InterruptedException {
        willThrow(new RuntimeException()).given(outbound).call();

        HystrixCircuitBreaker circuitBreaker = getCircuitBreaker();

        // demonstrates circuit is actually closed
        assertFalse(circuitBreaker.isOpen());
        assertTrue(circuitBreaker.allowRequest());

        try {
            myService.process();
            fail("unexpected");
        } catch (RuntimeException exception) {
            waitUntilCircuitBreakerOpens();
            assertTrue(circuitBreaker.isOpen());
            assertFalse(circuitBreaker.allowRequest());
        }
    }

    private void waitUntilCircuitBreakerOpens() throws InterruptedException {
        /* one second is almost sufficient
           borrowed from https://github.com/Netflix/Hystrix/blob/v1.5.5/hystrix-core/src/test/java/com/netflix/hystrix/HystrixCircuitBreakerTest.java#L140
         */
        Thread.sleep(1000);
    }

    private void resetHystrix() {
        Hystrix.reset();
    }

    private void warmUpCircuitBreaker() {
        myService.process();
    }

    public static HystrixCircuitBreaker getCircuitBreaker() {
        return HystrixCircuitBreaker.Factory.getInstance(getCommandKey());
    }

    private static HystrixCommandKey getCommandKey() {
        return HystrixCommandKey.Factory.asKey(HystrixDemo.MyService.COMMAND_KEY);
    }

    private void openCircuitBreakerAfterOneFailingRequest() {
        ConfigurationManager.getConfigInstance().setProperty("hystrix.command." + HystrixDemo.MyService.COMMAND_KEY + ".circuitBreaker.requestVolumeThreshold", 1);
    }
}   
Другие вопросы по тегам