概述
目前后端用的基本都是springCloud
体系;
平时在dev
环境开发时,会把自己的本地服务也注册上去,但是这样的话,在客户端调用时请求可能会打到自己本地,对客户端测试不太友好.
思路大致就是前端在请求头传入指定ip
,然后后端根据指定的ip
选择测试环境的服务实例
我的springcloud
版本用的loadbalancer
是spring-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
比如下面截图中所示