spring-cloud-gateway 自定义lb实现

时间:2022-06-27

官方的静态lb已经满足大部分场景

缺点是全静态,动态扩展能力不强

动态扩展有两个方向

方式一 是聚焦于spring-cloud-gateway本身,实现一些自定义的方法
方式二 是依赖spring-cloud的生态,consul/nacos注册中心,或config配置中心,bus事件队列动态更新配置等

做es的网关依赖spring-cloud,太重,方案放弃了

个人一开始以为没有静态lb的支持,所以直接就看方式一,所实际这个需求,静态基本就满足,但也不算完全是白折腾,至少更熟悉了spring-cloud-gateway的代码和机制

server:
  port: 9208
spring:
  cloud:
    gateway:
      enabled: true
    discovery:
      client:
        simple:
          instances:
            es3[0]:
              uri: http://127.0.0.1:19200
            es3[1]:
              uri: http://127.0.0.1:19201
            es3[2]:
              uri: http://127.0.0.1:19202
          order: 1

spring-cloud-gateway 原生本地代理,配置resource即可

纯静态有两个缺点
1 无法动态添加或删除节点,如instances下 添加es[3] ,删除可以自动检测屏蔽,添加就不好做了
2 即使无法动态添加,手动更改配置,再重启节点也满足要求

自定义方式一

实现ServiceInstanceListSupplier

public class CustomServiceInstanceListSupplier  implements ServiceInstanceListSupplier {

    public CustomServiceInstanceListSupplier() {
    }
    private String aaaa;

    @Override
    public String getServiceId() {
        return "es2";
    }

    @Override
    public Flux<List<ServiceInstance>> get() {
        List<ServiceInstance> sis=new ArrayList<>();
        sis.add(0, new DefaultServiceInstance("instanceId-4","es2","127.0.0.1",9204,false));
        sis.add(1, new DefaultServiceInstance("instanceId-5","es2","127.0.0.1",9205,false));
        System.out.println("get()");
        return Flux.defer(() -> {
            return Flux.just(sis);
        });
    }

    @Override
    public Flux<List<ServiceInstance>> get(Request request) {
        return get();
    }
}
---
	@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
		return ServiceInstanceListSupplier.builder()
				.withBase(new CustomServiceInstanceListSupplier())
//				.withHints()
//				.withHealthChecks()
				.build(context);
	}

自定义方式二,与方式一类似

实现DiscoveryClient 并结合DiscoveryClientServiceInstanceListSupplier使用

public class CustomDiscoveryClient implements DiscoveryClient {
    private List<ServiceInstance> instances;
    private String myServiceId;
    public CustomDiscoveryClient(List<ServiceInstance> instances) {
        this.myServiceId=myServiceId;
        this.instances=instances;
        loadInstancesFromRemote();
    }
    public void loadInstancesFromRemote(){
        List<ServiceInstance> serviceInstanceList2=new ArrayList<>();
        serviceInstanceList2.add(0, new DefaultServiceInstance("instanceId-0","ces2","127.0.0.1",9200,false));
        setInstances(serviceInstanceList2);
    }

    public String getMyServiceId() {
        return myServiceId;
    }

    public void setInstances(List<ServiceInstance> instances) {
        this.instances=instances;
    }

    @Override
    public String description() {
        return null;
    }

    @Override
    public List<ServiceInstance> getInstances(String serviceId) {
        return this.instances;
    }
    @Override
    public List<String> getServices() {
        return null;
    }

}

@Bean
	public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
			ConfigurableApplicationContext context) {
    		CustomDiscoveryClient cdc2=new CustomDiscoveryClient(null);
		  DiscoveryClientServiceInstanceListSupplier listSupplier=new        DiscoveryClientServiceInstanceListSupplier(cdc2,context.getEnvironment());
		return ServiceInstanceListSupplier.builder()
				.withBase(listSupplier)
				.build(context);
	}

再提供一个外部的webapi,添加或删除节点,则一个基本的动态lb功能就完备了

添加lb节点,删除lb节点,加载配置使lb变更生效

自定义方式三

@Bean
   ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                             LoadBalancerClientFactory loadBalancerClientFactory) {
      return new RandomLoadBalancer(loadBalancerClientFactory
            "es");
   }

方式三功能最细致

简而方式,方式一/方式二,都是注册全局的ServiceInstanceListSupplier,全局生效

方式三,自定义返回 RandomLoadBalancer,构造函数包含ServiceInstanceListSupplier,可以在外层生成对应的ServiceInstanceListSupplier

public RandomLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }

也可以完全不依赖官方的ServiceInstanceListSupplier,实现完全自定义的ReactorLoadBalancer

【推荐】 SpringCloud NamedContextFactory 原理与使用
【推荐】 java算法 -余弦相似度计算字符串相似率
【推荐】 python、java、c、go执行速度对比
【推荐】 密码学在互联网业务中的点滴使用