微服务环境下,多位开发开发服务,要不服务调到线上,或者调到同事服务上,导致莫名其妙的结果反映。
这里借鉴网上相关的文章,结合自身微服务配置,记录。
1.服务项目的pom增加配置:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2、服务配置文件增加配置
spring:
cloud:
loadbalancer:
local-first: true
nacos:
enabled: true
3、优先本地负载相关代码:
//引入配置
@Import({LocalFirstLoadBalancerNacosAutoConfiguration.class})
public class Application {
}
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that sets up LoadBalancer for Nacos.
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@LoadBalancerClients(value = {
@LoadBalancerClient(name = "datam", configuration = NacosLocalFirstLoadBalancerClientConfiguration.class)
})
public class LocalFirstLoadBalancerNacosAutoConfiguration {
}
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancerClientConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
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.context.annotation.Import;
import org.springframework.core.env.Environment;
/**
* nacos 负载均衡同IP 同区域有限
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
// 这里引入 nacos 默认客户端配置,否则的话需要添加 配置 spring.cloud.loadbalancer.nacos.enabled = true
@Import(NacosLoadBalancerClientConfiguration.class)
public class NacosLocalFirstLoadBalancerClientConfiguration{
private static final Logger log = LoggerFactory.getLogger(NacosLocalFirstLoadBalancerClientConfiguration.class);
/**
* 本地优先策略
* @param environment 环境变量
* @param loadBalancerClientFactory 工厂
* @param nacosDiscoveryProperties 属性
* @return ReactorLoadBalancer
*/
@Bean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.local-first", havingValue = "true")
public ReactorLoadBalancer<ServiceInstance> nacosLocalFirstLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory,
NacosDiscoveryProperties nacosDiscoveryProperties){
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
if(log.isDebugEnabled()) {
log.debug("Use nacos local first load balancer for {} service", name);
}
return new NacosLocalFirstLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name, nacosDiscoveryProperties);
}
}
import cn.hutool.core.net.NetUtil;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.balancer.NacosBalancer;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* nacos 本地服务优先负载均衡器
*/
public class NacosLocalFirstLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Logger log = LoggerFactory.getLogger(NacosLocalFirstLoadBalancer.class);
private final String serviceId;
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private final NacosDiscoveryProperties nacosDiscoveryProperties;
private Set<String> localIps;
public NacosLocalFirstLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, NacosDiscoveryProperties nacosDiscoveryProperties) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
// 使用hutool 工具获取本机IP地址
this.localIps = NetUtil.localIpv4s();
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get().next().map(this::getInstanceResponse);
}
/**
* 优先获取与本地IP一致的服务,否则获取同一集群服务
*
* @param serviceInstances
* @return
*/
private Response<ServiceInstance> getInstanceResponse(
List<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
log.warn("No servers available for service: " + this.serviceId);
return new EmptyResponse();
}
// 过滤与本机IP地址一样的服务实例
if (!CollectionUtils.isEmpty(this.localIps)) {
for (ServiceInstance instance : serviceInstances) {
String host = instance.getHost();
if (this.localIps.contains(host)) {
return new DefaultResponse(instance);
}
}
}
return this.getClusterInstanceResponse(serviceInstances);
}
/**
* 同一集群下优先获取
*
* @param serviceInstances
* @return
*/
private Response<ServiceInstance> getClusterInstanceResponse(
List<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
log.warn("No servers available for service: " + this.serviceId);
return new EmptyResponse();
}
try {
String clusterName = this.nacosDiscoveryProperties.getClusterName();
List<ServiceInstance> instancesToChoose = serviceInstances;
if (StringUtils.isNotBlank(clusterName)) {
List<ServiceInstance> sameClusterInstances = serviceInstances.stream()
.filter(serviceInstance -> {
String cluster = serviceInstance.getMetadata().get("nacos.cluster");
return StringUtils.equals(cluster, clusterName);
}).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(sameClusterInstances)) {
instancesToChoose = sameClusterInstances;
}
} else {
log.warn(
"A cross-cluster call occurs,name = {}, clusterName = {}, instance = {}",
serviceId, clusterName, serviceInstances);
}
ServiceInstance instance = NacosBalancer.getHostByRandomWeight3(instancesToChoose);
return new DefaultResponse(instance);
} catch (Exception e) {
log.warn("NacosLoadBalancer error", e);
return null;
}
}
}