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

比如下面截图中所示

相关推荐
vx_Biye_Design9 小时前
【关注可免费领取源码】房屋出租系统的设计与实现--毕设附源码40805
java·spring boot·spring·spring cloud·servlet·eclipse·课程设计
Volunteer Technology13 小时前
sentinel基本操作
spring cloud·sentinel
Dragon Wu13 小时前
Spring Security Oauth2.1 授权码模式实现前后端分离的方案
java·spring boot·后端·spring cloud·springboot·springcloud
vx_Biye_Design14 小时前
基于Spring Boot+vue的湖北旅游景点门票预约平台的设计--毕设附源码29593
java·vue.js·spring boot·spring cloud·servlet·eclipse·课程设计
是阿楷啊16 小时前
Java大厂面试场景:音视频场景中的Spring Boot与微服务实战
spring boot·redis·spring cloud·微服务·grafana·prometheus·java面试
岁岁种桃花儿1 天前
SpringCloud从入门到上天:Nacos做微服务注册中心
java·spring cloud·微服务
瑶山2 天前
Spring Cloud微服务搭建三、分布式任务调度XXL-JOB
java·spring cloud·微服务·xxljob
笃行客从不躺平2 天前
Token 复习
java·分布式·spring cloud
键盘帽子2 天前
多线程情况下长连接中的session并发问题
java·开发语言·spring boot·spring·spring cloud
没有bug.的程序员3 天前
Docker 与 K8s 生产级实战:从镜像极致优化到集群自动化部署全流程
spring cloud·docker·kubernetes·自动化·k8s·镜像·集群自动化