本文基于 Spring Cloud Hoxton.SR3
Spring Cloud Loadbalancer
是Spring Cloud官方提供的客户端负载均衡器.
文档及源码均在Spring Cloud Commons
下. 由于spring-cloud-starter-netflix-ribbon
已经处于维护模式, 所以了解一下正在发展中的Spring Cloud Loadbalancer
也说的过去.
整合过程中吧就发现...跑题了, 既然是配置教程那么咱就开始吧. 默认您对Gateway、Nacos有所了解, 并已经建立基本的工程.
Maven依赖
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.cloud
spring-cloud-starter-loadbalancer
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
2.2.0.RELEASE
实现 NacosWeightRandomLoadBalancer
主要实现ReactorServiceInstanceLoadBalancer#choose(Request request)
方法, 在其中参考(Ctrl C-V
) alibaba-nacos-discovery 内部的权重随机方式实现.
package com.kapukapu.gateway.config.loadbalancer;
import java.util.List;
import java.util.stream.Collectors;
import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import com.alibaba.nacos.client.naming.utils.Chooser;
import com.alibaba.nacos.client.naming.utils.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
/**
* A Weight-based implementation of {@link ReactorServiceInstanceLoadBalancer}.
*
* @author sivan757
*/
public class NacosWeightRandomLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Log log = LogFactory.getLog(NacosWeightRandomLoadBalancer.class);
private ObjectProvider serviceInstanceListSupplierProvider;
private final String serviceId;
/**
* @param serviceInstanceListSupplierProvider a provider of
* {@link ServiceInstanceListSupplier} that will be used to get available
* instances
* @param serviceId id of the service for which to choose an instance
*/
public NacosWeightRandomLoadBalancer(
ObjectProvider serviceInstanceListSupplierProvider,
String serviceId) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}
@Override
public Mono> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get().next().map(this::getInstanceResponse);
}
private Response getInstanceResponse(
List instances) {
if (instances.isEmpty()) {
log.warn("No servers available for service: " + this.serviceId);
return new EmptyResponse();
}
ServiceInstance instance = getHostByRandomWeight(instances);
return new DefaultResponse(instance);
}
/**
* Return one {@link ServiceInstance} from the host list by random-weight.
*
* @param serviceInstances The list of the instance.
* @return The random-weight result of the instance.
* @see com.alibaba.nacos.client.naming.core.Balancer#getHostByRandomWeight
*/
protected ServiceInstance getHostByRandomWeight(
List serviceInstances) {
log.debug("entry randomWithWeight");
if (serviceInstances == null || serviceInstances.size() == 0) {
log.debug("serviceInstances == null || serviceInstances.size() == 0");
return null;
}
Chooser instanceChooser = new Chooser<>(
"com.kapukapu");
List> hostsWithWeight = serviceInstances.stream()
.map(serviceInstance -> new Pair<>(serviceInstance,
getWeight(serviceInstance)))
.collect(Collectors.toList());
instanceChooser.refresh(hostsWithWeight);
log.debug("refresh instanceChooser");
return instanceChooser.randomWithWeight();
}
/**
* Get {@link ServiceInstance} weight metadata.
*
* @param serviceInstance instance
* @return The weight of the instance.
*
* @see NacosServiceDiscovery#hostToServiceInstance
*/
protected double getWeight(ServiceInstance serviceInstance) {
return Double.parseDouble(serviceInstance.getMetadata().get("nacos.weight"));
}
}
配置NacosWeightRandomLoadBalancer
通过@LoadBalancerClients
注解设置自定义的配置文件.
/**
* @author sivan757
*/
@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerConfig.class)
public class NacosLoadBalancerConfig {
@Bean
public ReactorLoadBalancer reactorServiceInstanceLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new NacosWeightRandomLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
到这里配置就结束了, 你可以在idea中快速的按下ctrl+d
愉快的尝试了.
当然前提是要在Nacos中注册几个服务并调整权重比例, 在gateway中配置上几个routers, 打开测试工具对着gateway发起冲锋.
示例
一个栗子
参考
- spring-cloud-commons-2.2.2.RELEASE-Passing Your Own Spring Cloud LoadBalancer Configuration