GateWay 转发服务到指定ip上

1,201 阅读2分钟

   背景: 

     公司采用网上开源的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上。

(每个人可以按照自己的情况测试下)

  结语: 

     上面这些照着百度上的东西凑出来的,勉强能用。后面还是要多花时间,了解下工作原理。

参考:

   SpringCloud Gateway 团队研发协同debug,解决方案 - 掘金 (juejin.cn)