SpringCloud升级之路2020.0.x版-24.测试SPLoadBalancer

识字粗堪供赋役,不须辛苦慕公卿。这篇文章主要讲述SpringCloud升级之路2020.0.x版-24.测试SPLoadBalancer相关的知识,希望能为你提供帮助。

SpringCloud升级之路2020.0.x版-24.测试SPLoadBalancer

文章图片

通过单元测试,我们也可以了解下一般我们实现 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()); } } } }

运行测试,测试通过。
SpringCloud升级之路2020.0.x版-24.测试SPLoadBalancer

文章图片

我们这一节使用单元测试验证我们要实现的这些功能是否有效。下一节,我们将开始分析同步环境下的 Http 客户端,Open-Feign Client。
【SpringCloud升级之路2020.0.x版-24.测试SPLoadBalancer】
SpringCloud升级之路2020.0.x版-24.测试SPLoadBalancer

文章图片


    推荐阅读