115的资源怎么找
bili_87429080123
2021年11月18日 10:56

并且资源,《PPS33A》幑与重试配置不同的是,通过系列前面的源码分析,我们知道 spring-cloud-openfeign 的 FeignClient 其实是懒加载的。所以我们实现的断路器也是懒加载的,需要先调用,之后才会初始化断路器。所以这里我们需要先进行调用之后,再验证断路器配置。

首先定义两个 FeignClient,微服务分别是 testService1 和 testService2,contextId 分别是 testService1Client 和 testService2Client

@FeignClient(name = "testService1&#​34;, contextId = "testService1Client&#​34;)

public interface TestService1Client {

    @GetMapping("/anything&#​34;)

    HttpBinAnythingResponse anything();

}

@FeignClient(name = "testService2&#​34;, contextId = "testService2Client&#​34;)

    public interface TestService2Client {

        @GetMapping("/anything&#​34;)

        HttpBinAnythingResponse anything();

}

然后,我们增加 Spring 配置,并且给两个微服务都添加一个实例,使用 SpringExtension 编写单元测试类:

//SpringExtension也包含了 Mockito 相关的 Extension,所以 @Mock 等注解也生效了

@ExtendWith(SpringExtension.class)

@SpringBootTest(properties = {

        //默认请求重试次数为 3

        "resilience4j.retry.configs.default.maxAttempts=3",

        // testService2Client 里面的所有方法请求重试次数为 2

        "resilience4j.retry.configs.testService2Client.maxAttempts=2",

        //默认断路器配置

        "resilience4j.circuitbreaker.configs.default.slidingWindowSize=5",

        "resilience4j.circuitbreaker.configs.default.minimumNumberOfCalls=2",

        //testService2Client 的 断路器配置

        "resilience4j.circuitbreaker.configs.testService2Client.failureRateThreshold=30",

        "resilience4j.circuitbreaker.configs.testService2Client.minimumNumberOfCalls=10",

        

})

@Log4j2

public class OpenFeignClientTest {

    @SpringBootapplication

    @Configuration

    public static class app {

        @Bean

        public DiscoveryClient discoveryClient() {

            //模拟两个服务实例

            ServiceInstance service1Instance1 = Mockito.spy(ServiceInstance.class);

            ServiceInstance service2Instance2 = Mockito.spy(ServiceInstance.class);

            Map<String, String> zone1 = Map.ofEntries(

                    Map.entry("zone&#​34;, "zone1&#​34;)

            );

            when(service1Instance1.getMetadata()).thenReturn(zone1);

            when(service1Instance1.getInstanceId()).thenReturn("service1Instance1&#​34;);

            when(service1Instance1.getHost()).thenReturn("www.httpbin.org&#​34;);

            when(service1Instance1.getPort()).thenReturn(80);

            when(service2Instance2.getInstanceId()).thenReturn("service1Instance2&#​34;);

            when(service2Instance2.getHost()).thenReturn("httpbin.org&#​34;);

            when(service2Instance2.getPort()).thenReturn(80);

            DiscoveryClient spy = Mockito.spy(DiscoveryClient.class);

            Mockito.when(spy.getInstances("testService1&#​34;))

                    .thenReturn(List.of(service1Instance1));

            Mockito.when(spy.getInstances("testService2&#​34;))

                    .thenReturn(List.of(service2Instance2));

            return spy;

        }

    }

}

编写测试代码,验证配置正确:

@Test

    public void testConfigureCircuitBreaker() {

        //防止断路器影响

        circuitBreakerRegistry.getAllCircuitBreakers().asJava().forEach(CircuitBreaker::reset);

        //调用下这两个 FeignClient 确保对应的 NamedContext 被初始化

        testService1Client.anything();

        testService2Client.anything();

        //验证断路器的实际配置,符合我们的填入的配置

        List<CircuitBreaker> circuitBreakers = circuitBreakerRegistry.getAllCircuitBreakers().asJava();

        Set<String> collect = circuitBreakers.stream().map(CircuitBreaker::getName)

                .filter(name -> {

                    try {

                        return name.contains(TestService1Client.class.getMethod("anything&#​34;).toGenericString())

                                || name.contains(TestService2Client.class.getMethod("anything&#​34;).toGenericString());

                    } catch (NoSuchMethodException e) {

                        return false;

                    }

                }).collect(Collectors.toSet());

        Assertions.assertEquals(collect.size(), 2);

        circuitBreakers.forEach(circuitBreaker -> {

            if (circuitBreaker.getName().contains(TestService1Client.class.getName())) {

                Assertions.assertEquals((int) circuitBreaker.getCircuitBreakerConfig().getFailureRateThreshold(), (int) DEFAULT_FAILURE_RATE_THRESHOLD);

                Assertions.assertEquals(circuitBreaker.getCircuitBreakerConfig().getMinimumNumberOfCalls(), DEFAULT_MINIMUM_NUMBER_OF_CALLS);

            } else if (circuitBreaker.getName().contains(TestService2Client.class.getName())) {

                Assertions.assertEquals((int) circuitBreaker.getCircuitBreakerConfig().getFailureRateThreshold(), (int) TEST_SERVICE_2_FAILURE_RATE_THRESHOLD);

                Assertions.assertEquals(circuitBreaker.getCircuitBreakerConfig().getMinimumNumberOfCalls(), TEST_SERVICE_2_MINIMUM_NUMBER_OF_CALLS);

            }

        });

    }

验证断路器是基于服务和方法打开的。

我们给 TestService1Client 添加一个方法:

@GetMapping("/status/500&#​34;)

String testCircuitBreakerStatus500();

这个方法一定会调用失败,从而导致断路器打开。经过 2 次失败以上后(因为配置最少触发断路器打开的请求个数为 2),验证断路器状态:

@Test

public void testCircuitBreakerOpenBasedOnServiceAndMethod() {

    //防止断路器影响

    circuitBreakerRegistry.getAllCircuitBreakers().asJava().forEach(CircuitBreaker::reset);

    AtomicBoolean passed = new AtomicBoolean(false);

    for (int i = 0; i < 10; i++) {

        //多次调用会导致断路器打开

        try {

            System.out.println(testService1Client.testCircuitBreakerStatus500());

        } catch(Exception e) {}

        List<CircuitBreaker> circuitBreakers = circuitBreakerRegistry.getAllCircuitBreakers().asJava();

        circuitBreakers.stream().filter(circuitBreaker -> {

            return circuitBreaker.getName().contains("testCircuitBreakerStatus500&#​34;)

                    && circuitBreaker.getName().contains("TestService1Client&#​34;);

        }).findFirst().ifPresent(circuitBreaker -> {

            //验证对应微服务和方法的断路器被打开

            if (circuitBreaker.getState().equals(CircuitBreaker.State.OPEN)) {

                passed.set(true);

                //断路器打开后,调用其他方法,不会抛出断路器打开异常

                testService1Client.testAnything();

            }

        });

    }

    

    Assertions.assertTrue(passed.get());

}

这样,我们就成功验证了,验证断路器是基于服务和方法打开的。