专栏目录
44.避免链路信息丢失做的设计(2) 41. SpringCloudGateway 基本流程讲解(1) 39. 改造 resilience4j 粘合 WebClient 9.如何理解并定制一个Spring Cloud组件 2.微服务框架需要考虑的问题 45. 实现公共日志记录 32. 改进负载均衡算法 31. FeignClient 实现断路器以及线程隔离限流的思路 13.UnderTow 核心配置 5.所有项目的parent与spring-framework-common说明 4.maven依赖回顾以及项目框架结构 30. FeignClient 实现重试 35. 验证线程隔离正确性 43.为何 SpringCloudGateway 中会有链路信息丢失 26.OpenFeign的组件 8.理解 NamedContextFactory 37. 实现异步的客户端封装配置管理的意义与设计 38. 实现自定义 WebClient 的 NamedContextFactory 23.订制Spring Cloud LoadBalancer 42.SpringCloudGateway 现有的可供分析的请求日志以及缺陷 14.UnderTow AccessLog 配置介绍 10.使用Log4j2以及一些核心配置 21.Spring Cloud LoadBalancer简介 40. spock 单元测试封装的 WebClient(下) 20. 启动一个 Eureka Server 集群 17.Eureka的实例配置 11.Log4j2 监控相关 15.UnderTow 订制 18.Eureka的客户端核心设计和配置 28.OpenFeign的生命周期-进行调用 1. 背景 41. SpringCloudGateway 基本流程讲解(2) 3.Eureka Server 与 API 网关要考虑的问题 44.避免链路信息丢失做的设计(1) 16.Eureka架构和核心概念 22.Spring Cloud LoadBalancer核心源码 27.OpenFeign的生命周期-创建代理 19.Eureka的服务端设计与配置 24.测试Spring Cloud LoadBalancer 29.Spring Cloud OpenFeign 的解析(1) 25.OpenFeign简介与使用 33. 实现重试、断路器以及线程隔离源码 40. spock 单元测试封装的 WebClient(上) 7.从Bean到SpringCloud 12.UnderTow 简介与内部原理 6.微服务特性相关的依赖说明 34.验证重试配置正确性 36. 验证断路器正确性

24.测试Spring Cloud LoadBalancer

unknown
• 阅读 1284

24.测试Spring Cloud LoadBalancer

本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford

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

运行测试,测试通过。

24.测试Spring Cloud LoadBalancer

我们这一节使用单元测试验证我们要实现的这些功能是否有效。下一节,我们将开始分析同步环境下的 Http 客户端,Open-Feign Client。

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

24.测试Spring Cloud LoadBalancer

点赞
收藏
评论区
推荐文章

暂无数据

unknown
unknown
Lv1
男 · rrrr · rrrrrrrr
rrrrr
文章
0
粉丝
17
获赞
0
热门文章

暂无数据