Nacos 自定义负载均衡,优先使用同IP服务
在微服务开发过程中,随着微服务数量越来越多,不可能将所有的微服务都在本地启用然后进行调试。最好的方式是需要调试那个服务就启动那个服务,所有的服务都使用开发Nacos,本地需要搭建nacos。
使用nacos在开发微服务的过程中,如果多人同时开发使用同一台服务就会导致服务调用错乱。也不知道自己的请求到达了那个服务。
如果在开发过程中,每个人都可以只调用自己的服务,那样调试代码就会很舒服。
鉴于以上问题,现有两种解决方案
方案一、使用nacos不同集群
每个开发都在配置文件中配置属于自己的集群,那么在进行调试代码时nacos会优先使用相同集群中的服务,如果同集群中服务找不到会返回其他集群中所有实例;这样每个人都只调用自己的服务,也能解决问题。但是会有多分配置文件。也不是太完美;
spring:
cloud:
loadbalancer.nacos.enabled: true
nacos:
discovery:
server-addr: 127.0.0.1:8848
group: dev
# 指定集群的名称,每个人都有自己一份
cluster-name: local
方案二、自定义nacos负载均衡机制
1、自定义nacos负载均衡器
自定义nacos服务加载机制,优先使用与本地同IP的服务。本地找不到则使用同集群,然后在使用其他集群服务
自定义负载均衡代码如下:
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;
}
}
}
以上代码大部分来源于 com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancer 添加了本地优先过滤规则
2、注册自定义负载均衡器到LoadBalancerClients中
使用自定义负载均衡器覆盖默认配置
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);
}
}
3、添加自动发现类
前两部代码不能直接添加到spring bean 中,否则在引用时获取不到请求服务名称,容易报空指针异常;
需要通过@LoadBalancerClients 注解注入自定义的负载均衡器
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that sets up LoadBalancer for Nacos.
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@LoadBalancerClients(defaultConfiguration = NacosLocalFirstLoadBalancerClientConfiguration.class)
public class LocalFirstLoadBalancerNacosAutoConfiguration {
}
4、如果需要自动装配
resources/META-INF/spring.factories文章来源:https://www.toymoban.com/news/detail-804297.html
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.commmon.LocalFirstLoadBalancerNacosAutoConfiguration
经过上述配置之后,就可以在开发微服务中与同事一起调试代码了,也不会出现调用串了问题。文章来源地址https://www.toymoban.com/news/detail-804297.html
到了这里,关于Nacos 自定义负载均衡,优先使用同IP服务(本地服务优先调用)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!