文章目录
- 🌕先说痛点
- 🍁解决思路(使用自定义负载均衡器)
- 🍁不知道怎么写?(那就看看源码参考下)
- 🍁动手时间 (模仿源码写一个)
- 🍁最终(完美解决)
🌕先说痛点
- 问题描述
同一台nacos 服务 多个开发人员在本地开发
,- 就会出现一个问题:也就是你本来想在本地调试你修改的代码,发现服务调到别的同事的服务上去了,导致调试很麻烦
- 举个例子:有
1
和2
两个开发人员,分别启动了A1,A2 B1 B2
服务, 然后A1
就有可能调用调B2
服务上,我预期的是A1
调用在B1
服务上 优先使用同IP服务(本地服务优先调用)
🍁解决思路(使用自定义负载均衡器)
自定义负载均衡器要怎么写
,
简单看下他已有负载均衡器是怎么写的1️⃣
RandomLoadBalancer
:基于随机访问的负载均衡策略
随机地从候选服务实例中选择一个实例来处理请求。2️⃣
NacosLoadBalancer
:基于Nacos权重的负载均衡策略:
根据服务实例的权重来决定请求的分配比例。权重越高的实例将获得更多的请求。3️⃣
RoundRobinLoadBalancer
:基于轮询的负载均衡策略
按顺序轮询每一个实例
🍁不知道怎么写?(那就看看源码参考下)
我这边是随便选了一个: 下面截图是这个类
RoundRobinLoadBalancer
🍁动手时间 (模仿源码写一个)
想要修改负载逻辑只需要修改这个两个方法就可以
getInstanceResponse
和getClusterInstanceResponse
package com.common.star.base.configuration;
import cn.hutool.core.net.NetUtil;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.excel.util.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* nacos 自定义负载均衡器
*
* @Author: hrd
* @CreateTime: 2023/7/1 14:30
* @Description:
*/
@Slf4j
public class MySameIPRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
final AtomicInteger position;
final String serviceId;
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private final NacosDiscoveryProperties nacosDiscoveryProperties;
private final Set<String> localIps;
public MySameIPRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, NacosDiscoveryProperties nacosDiscoveryProperties) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.localIps = NetUtil.localIpv4s();
this.position = new AtomicInteger(new Random().nextInt(1000));
}
@SuppressWarnings("rawtypes")
@Override
// see original
// https://github.com/Netflix/ocelli/blob/master/ocelli-core/
// src/main/java/netflix/ocelli/loadbalancer/RoundRobinLoadBalancer.java
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> serviceInstances) {
if (serviceInstances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + serviceId);
}
return new EmptyResponse();
}
log.info("--------------------------");
// 过滤与本机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);
}
int pos = Math.abs(this.position.incrementAndGet());
// 同一个集群下轮询
ServiceInstance instance = instancesToChoose.get(pos % instancesToChoose.size());
return new DefaultResponse(instance);
} catch (Exception e) {
log.warn("NacosLoadBalancer error", e);
return null;
}
}
}
我这里判断了只有本地才走自定义负载均衡器
package com.common.star.base.configuration;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
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;
/**
* 本地才走自定义负载均衡
*
* @Author: hrd
* @CreateTime: 2023/7/1 14:50
* @Description:
*/
@Slf4j
@Configuration(proxyBeanMethods = false)
public class MyLoadBalancerConfig {
@Value("${spring.profiles.active}")
private String active;
/**
* 本地优先策略
*
* @param environment 环境变量
* @param loadBalancerClientFactory 工厂
* @param nacosDiscoveryProperties 属性
* @return ReactorLoadBalancer
*/
@Bean
public ReactorLoadBalancer<ServiceInstance> nacosLocalFirstLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory,
NacosDiscoveryProperties nacosDiscoveryProperties) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
ObjectProvider<ServiceInstanceListSupplier> lazyProvider = loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class);
if ("dev".equals(active)) {
log.info("Use nacos local first load balancer for {} service", name);
return new MySameIPRoundRobinLoadBalancer(lazyProvider, name, nacosDiscoveryProperties);
}
return new RoundRobinLoadBalancer(lazyProvider, name);
}
}
把自定义负载均衡器 设置为默认的
package com.common.star.base.configuration;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
/**
* @Author: hrd
* @CreateTime: 2023/7/1 16:47
* @Description:
*/
@LoadBalancerClients(defaultConfiguration = MyLoadBalancerConfig.class)
public class MyLoadBalancerClients {
}
🍁最终(完美解决)
- 可以看到最后返回服务就是我调用服务同一ip的