自定义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

比如下面截图中所示

相关推荐
luckywuxn6 小时前
Spring Cloud Alibaba、Spring Cloud 与 Spring Boot各版本的对应关系
spring boot·spring·spring cloud
wclass-zhengge1 天前
SpringCloud篇(服务网关 - GateWay)
spring boot·spring cloud·gateway
荆州克莱1 天前
Redis | Redis常用命令及示例总结(API)
spring boot·spring·spring cloud·css3·技术
RainbowSea1 天前
5. Spring Cloud OpenFeign 声明式 WebService 客户端的超详细使用
java·spring·spring cloud
听潮阁1 天前
【SpringCloud详细教程】-02-微服务环境搭建
spring·spring cloud·微服务
阿维的博客日记1 天前
java八股-SpringCloud微服务-Eureka理论
spring cloud·eureka·nacos
跳跳的向阳花1 天前
03-02、SpringCloud第二章,Eureka服务的注册与发现
spring·spring cloud·eureka
泰山小张只吃荷园1 天前
快速入门消息队列MQ、RabbitMQ
java·spring boot·分布式·spring·spring cloud·rabbitmq·java-rabbitmq
customer081 天前
【开源免费】基于SpringBoot+Vue.JS网上订餐系统(JAVA毕业设计)
java·jvm·vue.js·spring boot·spring cloud·java-ee·开源
不能只会打代码1 天前
Spring Boot 和 Spring Cloud 微服务开发实践详解
java·spring boot·spring cloud·微服务