Nacos负载均衡配置指导
引入Loadbalancer
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
引入loadbalance后SpringCloud会使用ip轮训策略调用nacos中的服务,我们可以通过修改配置来定义负载均衡策略。我们也可以使用Ribbon来实现,目前ribbon已不再维护,不推荐。
负载均衡策略
1 Ribbon载均衡策略
Ribbon有多种负载均衡策略
- 随机 RandomRule
- 轮询 RoundRobinRule
- 重试 RetryRule
- 最低并发 BestAvailableRule
- 可用过滤 AvailabilityFilteringRule
- 响应时间加权重 ResponseTimeWeightedRule
- 区域权重 ZoneAvoidanceRule
2. LoadBalancer载均衡策略
LoadBalancer貌似只提供了两种负载均衡器,不指定的时候默认用的是轮询
- RandomLoadBalancer 随机
- RoundRobinLoadBalancer 轮询
- NacosLoadBalancer nacos实现的负载均衡, 同集群优先,同ip段优先
配置负载均衡策略
我们可以在springboot的@Configuration类上配置策略, 如下配置随机策略,同时优化http请求工具,采用RestTemplate实现。
@Configuration
@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerClientConfiguration.class)
public class GatewayConfig {
....
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
自定义负载均衡策略
典型场景:我们需要服务在负载时优先在相同集群服务,然后在进行随机。该场景我们推荐使用NacosLoadBalancer ,以下示例只是展示自定义方法,如有更个性化的策略,可以参考实现。不要忘记在config类上指定策略!!
**
* 自定义实现loadbanlance, 同一集群优先调用
*
* @author: PengHao
* @date: 2023/9/22 10:30:11
* @version: v1.0
*/
@Slf4j
public class NacosSameClusterWeightedRule implements ReactorServiceInstanceLoadBalancer {
// 注入当前服务的nacos的配置信息
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;
// loadbalancer 提供的访问的服务列表
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
public NacosSameClusterWeightedRule(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}
/**
* 服务器调用负载均衡时调的放啊
* 此处代码内容与 RandomLoadBalancer 一致
*/
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next().map((serviceInstances) -> {
return this.processInstanceResponse(supplier, serviceInstances);
});
}
/**
* 对负载均衡的服务进行筛选的方法
* 此处代码内容与 RandomLoadBalancer 一致
*/
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
/**
* 对负载均衡的服务进行筛选的方法
* 自定义
* 此处的 instances 实例列表 只会提供健康的实例 所以不需要担心如果实例无法访问的情况
*/
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
return new EmptyResponse();
}
// 获取当前服务所在的集群名称
String currentClusterName = nacosDiscoveryProperties.getClusterName();
log.debug("==> get current cluster name: {}.", currentClusterName);
// 过滤在同一集群下注册的服务 根据集群名称筛选的集合
List<ServiceInstance> sameClusterNameInstList = instances.stream().filter(i-> StringUtils.equals(i.getMetadata().get("nacos.cluster"),currentClusterName)).collect(Collectors.toList());
ServiceInstance sameClusterNameInst;
log.debug("==> get same cluster install size : {}.", sameClusterNameInstList.size());
if (sameClusterNameInstList.isEmpty()) {
// 如果为空,则根据权重直接过滤所有服务列表
sameClusterNameInst = getHostByRandomWeight(instances);
} else {
// 如果不为空,则根据权重直接过滤所在集群下的服务列表
sameClusterNameInst = getHostByRandomWeight(sameClusterNameInstList);
}
log.debug("==> get same cluster install host: {}.", sameClusterNameInst.getHost());
return new DefaultResponse(sameClusterNameInst);
}
private ServiceInstance getHostByRandomWeight(List<ServiceInstance> sameClusterNameInstList){
List<Instance> list = new ArrayList<>();
Map<String,ServiceInstance> dataMap = new HashMap<>();
// 此处将 ServiceInstance 转化为 Instance 是为了接下来调用nacos中的权重算法,由于入参不同,所以需要转换,此处建议打断电进行参数调试,以下是我目前为止所用到的参数,转化为map是为了最终方便获取取值到的服务对象
sameClusterNameInstList.forEach(i->{
Instance ins = new Instance();
Map<String, String> metadata = i.getMetadata();
ins.setInstanceId(metadata.get("nacos.instanceId"));
ins.setWeight(new BigDecimal(metadata.get("nacos.weight")).doubleValue());
ins.setClusterName(metadata.get("nacos.cluster"));
ins.setEphemeral(Boolean.parseBoolean(metadata.get("nacos.ephemeral")));
ins.setHealthy(Boolean.parseBoolean(metadata.get("nacos.healthy")));
ins.setPort(i.getPort());
ins.setIp(i.getHost());
ins.setServiceName(i.getServiceId());
ins.setMetadata(metadata);
list.add(ins);
// key为服务ID,值为服务对象
dataMap.put(metadata.get("nacos.instanceId"),i);
});
// 调用nacos官方提供的负载均衡权重算法
Instance hostByRandomWeightCopy = ExtendBalancer.getHostByRandomWeightCopy(list);
// 根据最终ID获取需要返回的实例对象
return dataMap.get(hostByRandomWeightCopy.getInstanceId());
}
}
class ExtendBalancer extends Balancer {
/**
* 根据权重选择随机选择一个
*/
public static Instance getHostByRandomWeightCopy(List<Instance> hosts) {
return getHostByRandomWeight(hosts);
}
}