背景:
公司采用网上开源的ruoyi微服务架构, 前后端联调的时候,多个相同的服务同时启动的话,因为gateWay默认的是轮询算法,前端请求不能正确的打到后端人员的机器上。后端需要启动gateway,auth,system三个微服务,再启动一个业务的微服务,一个电脑要跑至少四个服务,而且每人在nacos上还要注册一个自己的命名空间,代价太大。
目标:
大家公用一个开发的命名空间,由前端指定要访问的后端人员的ip,将服务转发到该ip上。
每个人只启动自己的业务微服务,其他服务放在服务器上启动,此策略只在开发和测试环境中起作用,正式环境还是采用轮询。
解决方案:
版本:
<spring-boot.version>2.6.4</spring-boot.version>
<spring-cloud.version>2021.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
<alibaba.nacos.version>2.0.4</alibaba.nacos.version>
1. 自定义负载均衡实例。
public class CustomLoadBalancerClient implements ReactorServiceInstanceLoadBalancer {
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private String serviceId;
public CustomLoadBalancerClient(
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId) {
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.serviceId = serviceId;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
RequestData requestData = ((RequestDataContext) request.getContext()).getClientRequest();
ServiceInstanceListSupplier supplier =
serviceInstanceListSupplierProvider.getIfAvailable(
NoopServiceInstanceListSupplier::new);
// 从请求头中获取指定转发的ip
String ip = requestData.getHeaders().getFirst("ip");
if (StringUtils.isNotBlank(ip)) {
return supplier.get(request)
.next()
.map(serviceInstances -> getInstanceResponse(serviceInstances, ip));
}
// 默认轮询策略
return new RoundRobinLoadBalancer(serviceInstanceListSupplierProvider, serviceId)
.choose(request);
}
private Response<ServiceInstance> getInstanceResponse(
List<ServiceInstance> instances, String ip) {
System.out.println("》》》》》》》》》进入自定义负载均衡策略");
List<ServiceInstance> fileterList = instances.stream().filter(a-> Objects.equals(ip, a.getHost())).collect(Collectors.toList());
if(MyCollectionUtils.isNotEmpty(fileterList)){
return new DefaultResponse(fileterList.get(0));
}
if(MyCollectionUtils.isNotEmpty(instances)){
return new DefaultResponse(instances.get(0));
}
return new EmptyResponse();
}
}
逻辑在 getInstanceResponse 这个方法里面,大体逻辑就是 判断集合中有没有ip是指定ip的实例,如果有,返回,如果没有,返回实例列表的第一个。
2. 注册Bean,注意没有@Configuration
public class MyLoadBalancerConfig {
@Bean
public ReactorServiceInstanceLoadBalancer customLoadBalancer(
Environment environment,
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new CustomLoadBalancerClient(serviceInstanceListSupplierProvider, name);
}
}
3. 通过@LoadBalancerClients 指定所需的微服务
@Configuration
@LoadBalancerClients({
@LoadBalancerClient(name = "ruoyi-system", configuration = MyLoadBalancerConfig.class),
})
@Profile({"dev","test"})
public class LoadBalancerConfig {
}
4. 工具类
public class MyCollectionUtils {
public static boolean isEmpty(Collection<?> collection){
if(null != collection && collection.size() > 0){
return false;
}
return true;
}
public static boolean isNotEmpty(Collection<?> collection){
return !isEmpty(collection);
}
}
测试:
在两台电脑上启动微服务,注册到nacos上,发送两次请求,两次请求都落到了指定ip上。
(每个人可以按照自己的情况测试下)
结语:
上面这些照着百度上的东西凑出来的,勉强能用。后面还是要多花时间,了解下工作原理。