自定义springCloudLoadbalancer简述

概述

目前后端用的基本都是springCloud体系;

平时在dev环境开发时,会把自己的本地服务也注册上去,但是这样的话,在客户端调用时请求可能会打到自己本地,对客户端测试不太友好.

思路大致就是前端在请求头传入指定ip,然后后端根据指定的ip选择测试环境的服务实例

我的springcloud版本用的loadbalancerspring-cloud-starter-loadbalancer,早期版本可能用的是ribbon,不过思路大致都是一致的。

jdk:1.8
springcloud版本:2021.0.5

服务实例选择策略简述

当前http调用服务时大多用的是open feign,在调用时一路debug就能看到源码的选择服务实例的过程了;

大致就是请求进来 -> 选择serviceId对应的负载均衡器 -> 根据负载均衡器的choose策略选择一个服务实例 -> 处理请求

关键代码
org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient#execute

选择服务实例

选择一个负载均衡器

默认是org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer

根据serviceId选择负载均衡器

看到这儿就知道大致是怎么一回事了

自定义服务选择策略

看完上完可以知道springCloud已经给我们留好了扩展口子

实现这个接口然后注册上去就可以使用了
org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer

可以参考默认的选择器
org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer

自定义负载均衡选择策略

java 复制代码
package org.xxx.xxx.loadbalance.core;

import cn.hutool.core.net.NetUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 自定义 SpringCloud 负载均衡算法
 *
 */
@Slf4j
@AllArgsConstructor
public class CustomSpringCloudLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private final String serviceId;

    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

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

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
                                                              List<ServiceInstance> serviceInstances) {
        Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("No servers available for service: " + serviceId);
            }
            return new EmptyResponse();
        }
        for (ServiceInstance instance : instances) {
            //自定义策略
            if (NetUtil.localIpv4s().contains(instance.getHost())) {
                return new DefaultResponse(instance);
            }
        }
        return new DefaultResponse(instances.get(ThreadLocalRandom.current().nextInt(instances.size())));
    }

}

注册自定义负载均衡选择策略class为springBean

java 复制代码
package org.xxx.xxx.loadbalance.config;

import org.xxx.xxx.loadbalance.core.CustomSpringCloudLoadBalancer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
 * 自定义负载均衡客户端配置
 */
@SuppressWarnings("all")
@Configuration(proxyBeanMethods = false)
public class CustomLoadBalanceClientConfiguration {

    @Bean
    @ConditionalOnBean(LoadBalancerClientFactory.class)
    public ReactorLoadBalancer<ServiceInstance> customLoadBalancer(Environment environment,
                                                                   LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new CustomSpringCloudLoadBalancer(name,
            loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class));
    }
}

serviceId绑定对应的负载均衡策略

java 复制代码
package com.xxx.xxx.security.config;

import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

/**
 * 负载均衡配置
 */
@Configuration
@LoadBalancerClients(
        value = {
                //指定
        @LoadBalancerClient(name = "name1", configuration = CustomLoadBalanceClientConfiguration.class),
        @LoadBalancerClient(name = "name2", configuration = CustomLoadBalanceClientConfiguration.class)
        },
        //默认;即没有命中上面的serviceId的指定策略时,默认走下面的配置
        defaultConfiguration = CustomLoadBalanceClientConfiguration.class
)
//只在测试环境使用
@Profile({"test"})
public class LoadBalancerConfig {


}

最后通过org.springframework.boot.autoconfigure.AutoConfiguration.imports加载LoadBalancerConfig

比如下面截图中所示

相关推荐
微三云 - 廖会灵 (私域系统开发)3 天前
电商系统微服务拆分实战:从单体到Spring Cloud的分布式事务踩坑与补偿方案
分布式·spring cloud·微服务
k↑3 天前
SpringCloud + React19 集成Scalar的API文档
后端·spring·spring cloud
LiaoWL1233 天前
【SpringCloud合集-02】Spring Cloud LoadBalancer 负载均衡器学习笔记
学习·spring cloud·负载均衡
9i编程5 天前
5.Nacos 配置中心化改造与短信 Debug 模式动态切换实践
后端·spring cloud
Devin~Y6 天前
抖音级短视频推荐与直播带货平台面试实战:从 Java 微服务到 RAG 智能客服全链路解析
java·spring boot·redis·spring cloud·kafka·agent·rag
江畔柳前堤7 天前
第17章:Docker 大厂面试题精选(腾讯/阿里/字节/美团)
运维·网络·spring cloud·docker·容器·eureka
吃饱了得干活13 天前
Spring Cloud Gateway 微服务网关:路由、断言、过滤器
java·spring cloud
慧一居士24 天前
Feign的GET请求如何传递对象参数?
java·spring cloud
我登哥MVP24 天前
SpringCloud Alibaba 核心组件解析:服务链路追踪
java·spring boot·后端·spring·spring cloud·java-ee·maven
慧一居士24 天前
SpringCloud 微服务Feigin 用的完整调用端和被调用的示例
java·spring cloud