本系列代码地址:https://github.com/HashZhang/...

通过单元测试,咱们也能够理解下个别咱们实现 spring cloud 自定义的根底组件,怎么去单元测试。

这里的单元测试次要测试三个场景:

  1. 只返回同一个 zone 下的实例,其余 zone 的不会返回
  2. 对于多个申请,每个申请返回的与上次的实例不同。
  3. 对于多线程的每个申请,如果重试,返回的都是不同的实例

同时,咱们也须要针对同步和异步两个配置,别离进行测试,同步和异步两种配置测试逻辑是一样的,只是测试的 Bean 不一样

  • 同步环境是 DiscoveryClient,异步环境是 ReactiveDiscoveryClient
  • 同步环境负载均衡器是 LoadBalancer,异步环境负载均衡器是 ReactiveLoadBalancer

同步测试代码请参考:LoadBalancerTest.java,异步测试代码请参考:LoadBalancerTest.java

咱们这里应用同步测试代码作为例子展现:

//SpringExtension也蕴含了MockitoJUnitRunner,所以 @Mock 等注解也失效了@ExtendWith(SpringExtension.class)@SpringBootTest(properties = {LoadBalancerEurekaAutoConfiguration.LOADBALANCER_ZONE + "=zone1"})public class LoadBalancerTest {    @EnableAutoConfiguration    @Configuration    public static class App {        @Bean        public DiscoveryClient myDiscoveryClient() {            ServiceInstance zone1Instance1 = Mockito.spy(ServiceInstance.class);            ServiceInstance zone1Instance2 = Mockito.spy(ServiceInstance.class);            ServiceInstance zone2Instance3 = Mockito.spy(ServiceInstance.class);            Map<String, String> zone1 = Map.ofEntries(                    Map.entry("zone", "zone1")            );            Map<String, String> zone2 = Map.ofEntries(                    Map.entry("zone", "zone2")            );            when(zone1Instance1.getMetadata()).thenReturn(zone1);            when(zone1Instance1.getInstanceId()).thenReturn("instance1");            when(zone1Instance2.getMetadata()).thenReturn(zone1);            when(zone1Instance2.getInstanceId()).thenReturn("instance2");            when(zone2Instance3.getMetadata()).thenReturn(zone2);            when(zone2Instance3.getInstanceId()).thenReturn("instance3");            DiscoveryClient spy = Mockito.spy(DiscoveryClient.class);            Mockito.when(spy.getInstances("testService"))                    .thenReturn(List.of(zone1Instance1, zone1Instance2, zone2Instance3));            return spy;        }    }    @SpyBean    private LoadBalancerClientFactory loadBalancerClientFactory;    @SpyBean    private Tracer tracer;    /**     * 只返回同一个 zone 下的实例     */    @Test    public void testFilteredByZone() {        ReactiveLoadBalancer<ServiceInstance> testService =                loadBalancerClientFactory.getInstance("testService");        for (int i = 0; i < 100; i++) {            ServiceInstance server = Mono.from(testService.choose()).block().getServer();            //必须处于和以后实例同一个zone下            Assertions.assertEquals(server.getMetadata().get("zone"), "zone1");        }    }    /**     * 返回不同的实例     */    @Test    public void testReturnNext() {        ReactiveLoadBalancer<ServiceInstance> testService =                loadBalancerClientFactory.getInstance("testService");        Span span = tracer.nextSpan();        for (int i = 0; i < 100; i++) {            try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {                ServiceInstance server1 = Mono.from(testService.choose()).block().getServer();                ServiceInstance server2 = Mono.from(testService.choose()).block().getServer();                //每次抉择的是不同实例                Assertions.assertNotEquals(server1.getInstanceId(), server2.getInstanceId());            }        }    }    /**     * 跨线程,默认状况下是可能返回同一实例的,在咱们的实现下,放弃     * span 则会返回下一个实例,这样保障多线程环境同一个 request 重试会返回下一实例     *     * @throws Exception     */    @Test    public void testSameSpanReturnNext() throws Exception {        Span span = tracer.nextSpan();        for (int i = 0; i < 100; i++) {            try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {                ReactiveLoadBalancer<ServiceInstance> testService =                        loadBalancerClientFactory.getInstance("testService");                ServiceInstance server1 = Mono.from(testService.choose()).block().getServer();                AtomicReference<ServiceInstance> server2 = new AtomicReference<>();                Thread thread = new Thread(() -> {                    try (Tracer.SpanInScope cleared2 = tracer.withSpanInScope(span)) {                        server2.set(Mono.from(testService.choose()).block().getServer());                    }                });                thread.start();                thread.join();                System.out.println(i);                Assertions.assertNotEquals(server1.getInstanceId(), server2.get().getInstanceId());            }        }    }}

运行测试,测试通过。

咱们这一节应用单元测试验证咱们要实现的这些性能是否无效。下一节,咱们将开始剖析同步环境下的 Http 客户端,Open-Feign Client。

微信搜寻“我的编程喵”关注公众号,每日一刷,轻松晋升技术,斩获各种offer