当前位置: 首页 > article >正文

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

自定义负载均衡选择策略

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

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绑定对应的负载均衡策略

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
比如下面截图中所示
在这里插入图片描述


http://www.kler.cn/a/386858.html

相关文章:

  • C#发票识别、发票查验接口集成、电子发票(航空运输电子行程单)
  • 【Pikachu】目录遍历实战
  • redhat虚拟机
  • 【Rust练习】21.动态数组 Vector
  • 定时器(QTimer)与随机数生成器(QRandomGenerator)的应用实践——Qt(C++)
  • Linux探秘坊-------1.系统核心的低语:基础指令的奥秘解析(1)
  • CMS垃圾回收流程的理解
  • 在线演示,开箱即用:传知平台让高质量内容与技术完美融合
  • 记一次宝塔centos出现Failed to start crond.service: Unit crond.service not found.解决
  • YOLOv11融合[ECCV2024]自调制特征聚合SMFA模块及相关改进思路|YOLO改进最简教程
  • 使用ookii-dialogs-wpf在WPF选择文件夹时能输入路径
  • 「Mac畅玩鸿蒙与硬件31」UI互动应用篇8 - 自定义评分星级组件
  • Flink on YARN是如何确定TaskManager个数的
  • [spark面试]spark与mapreduce的区别---在DAG方面
  • CI_CD
  • LabVIEW气体检测系统
  • 【Android】组件化开发入门
  • java-web-苍穹外卖-day2-上:测试阶段区分+开发工具区分
  • 在CentOS 7上安装Alist
  • 【elkb】kibana后台删除索引
  • Android OpenGL ES详解——纹理:纹理过滤GL_NEAREST和GL_LINEAR的区别
  • jmeter常用配置元件介绍总结之函数助手
  • Virtual Box 安装 Deepin 系统
  • Ubuntu 修改时区 同步时间
  • lua入门教程:lua函数
  • 【嵌入式开发——Linux操作系统】8进程间通信IPC和内核同步