SpringCloud LoadBalancer灰度策略實現(xiàn)

如何使用 Spring Cloud 2020 中重磅推薦的負載均衡器 Spring Cloud LoadBalancer (下文簡稱 SCL),如何擴展負載均衡策略? 你將從本文中獲取到答案

快速上手 SCL

  • 如果項目中想使用 SCL,則僅需要添加如下 maven 依賴即可
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
  • SCL 是構(gòu)建服務(wù)發(fā)現(xiàn)的基礎(chǔ)上,由于目前 Spring Cloud Alibaba 并未兼容 SCL (具體兼容方案可以參考 pig),當(dāng)然你可以選擇使用Eureka 測試。

  • 若將 RestTemplate 和 客戶端負載均衡結(jié)合使用,在 bean 定義上增加 @LoadBalanced 注解即可.

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}

個性化負載均衡策略

SCL 內(nèi)置的負載均衡策略
  • 目前版本 (spring cloud 2020) 內(nèi)置輪詢、隨機的負載均衡策略,默認輪詢策略。
  • 當(dāng)然可以通過 LoadBalancerClient 注解,指定服務(wù)級別的負載均衡策略
@LoadBalancerClient(value = "demo-provider", configuration = RandomLoadbalancerConfig.class)
public class RandomLoadbalancerConfig {
    @Bean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(
                loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

自定義負載均衡策略

  • 通過上文可知,目前 SCL 支持的負載均衡策略相較于 Ribbon 還是較少,需要開發(fā)者自行實現(xiàn),好在 SCL 提供了便捷的 API 方便擴展使用。 這里演示自定義一個基于注冊中心元數(shù)據(jù)的灰度負載均衡策略。

  • 定義灰度負載均衡策略

@Slf4j
public class GrayRoundRobinLoadBalancer extends RoundRobinLoadBalancer {

    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    private String serviceId;

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
                .getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next().map(serviceInstances -> getInstanceResponse(serviceInstances, request));
    }

    Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {

        // 注冊中心無可用實例 拋出異常
        if (CollUtil.isEmpty(instances)) {
            log.warn("No instance available {}", serviceId);
            return new EmptyResponse();
        }

        DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
        RequestData clientRequest = (RequestData) requestContext.getClientRequest();
        HttpHeaders headers = clientRequest.getHeaders();

        String reqVersion = headers.getFirst(CommonConstants.VERSION);
        if (StrUtil.isBlank(reqVersion)) {
            return super.choose(request).block();
        }

        // 遍歷可以實例元數(shù)據(jù),若匹配則返回此實例
        for (ServiceInstance instance : instances) {
            NacosServiceInstance nacosInstance = (NacosServiceInstance) instance;
            Map<String, String> metadata = nacosInstance.getMetadata();
            String targetVersion = MapUtil.getStr(metadata, CommonConstants.VERSION);
            if (reqVersion.equalsIgnoreCase(targetVersion)) {
                log.debug("gray requst match success :{} {}", reqVersion, nacosInstance);
                return new DefaultResponse(nacosInstance);
            }
        }
        // 降級策略,使用輪詢策略
        return super.choose(request).block();
    }
}
  • 針對客戶端注入灰度負載均衡策略
@LoadBalancerClient(value = "demo-provider", configuration = GrayRoundLoadbalancerConfig.class)
  • 服務(wù)實例定義版本號
  • 請求攜帶版本號,測試使用
curl --location --request GET 'http://localhost:6060/req?key=b' \
--header 'VERSION: b'

優(yōu)化負載均衡策略注入

  • 如上文所述,所有的個性化負載策略都需要手動通過 LoadBalancerClient 注入非常的不方便。 我們可以參考 LoadBalancerClients 的批量注入邏輯構(gòu)造自己的 BeanRegistrar
public class GrayLoadBalancerClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        Field[] fields = ReflectUtil.getFields(ServiceNameConstants.class);

        // 遍歷服務(wù)名稱,注入支持灰度策略的負載均衡器
        for (Field field : fields) {
            Object fieldValue = ReflectUtil.getFieldValue(ServiceNameConstants.class, field);
            registerClientConfiguration(registry, fieldValue, GrayLoadBalancerClientConfiguration.class);
        }
    }
}

>>> 源碼 https://gitee.com/log4j/pig,歡迎署名轉(zhuǎn)載 <<<

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容