spring-cloud-gateway 类nginx反向代理 无注册中心实现负载均衡

493 阅读3分钟

虽然spring-cloud-gateway再没有注册中心时也可以实现负载均衡但是不够灵活,需要写代码才可以,比如要路径 127.0.0.1:8080/baidu 访问百度 127.0.0.1:8080/bing 访问bing。

需要先创建两个类 并且 implements ServiceInstanceListSupplier

ServiceInstanceListSupplier类是 spring-cloud-starter-loadbalancer 中的一个接口,spring-cloud-starter-loadbalancer 为spring-cloud-gateway提供负载均衡的功能

百度负载均衡配置

public class BaiduServiceInstanceListSupplier implements ServiceInstanceListSupplier {

    @Override
    public Flux<List<ServiceInstance>> get() {
       List<ServiceInstance> list = new ArrayList<ServiceInstance>();
       list.add(new DefaultServiceInstance(this.getServiceId() + list.size(), this.getServiceId(), "www.baidu.com",
             443, true)); //这里可以填写多个地址实现负载均衡,作为例子只填写一个节点
       return Flux.just(list);
    }

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

}

bing负载均衡配置

 public class BingServiceInstanceListSupplier implements ServiceInstanceListSupplier {

    @Override
    public Flux<List<ServiceInstance>> get() {
       List<ServiceInstance> list = new ArrayList<ServiceInstance>();
       list.add(new DefaultServiceInstance(this.getServiceId() + list.size(), this.getServiceId(), "www.bing.com",
             443, true)); //这里可以填写多个地址实现负载均衡,作为例子只填写一个节点
       return Flux.just(list);
    }

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

}

以及配置类

@Configuration(proxyBeanMethods = false)
public class RouteConfig {

    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routesBuilder = builder.routes();
       return routesBuilder.route("baidu",r->r.path("/baidu").uri("lb://baidu")) //配置百度路劲
                .route("bing",r->r.path("/bing").uri("lb://bing")) //配置bing路劲
                .build();
        
    }
    
    @Bean
    @Primary
    public LoadBalancerClientFactory loadBalancerClientFactory(LoadBalancerClientsProperties properties) throws IOException {
        LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory(properties);
        List<LoadBalancerClientSpecification> list = new ArrayList<>();
        list.add(new LoadBalancerClientSpecification("baidu", new Class[]{BaiduServiceInstanceListSupplier.class})); //百度
        list.add(new LoadBalancerClientSpecification("bing", new Class[]{BingServiceInstanceListSupplier.class})); //bing
        clientFactory.setConfigurations(list);
        return clientFactory;
    }

}

通过上述代码,就可以实现对百度已经bing的负载均衡。这些都是通过代码里面实现的,更好的方式是通过配置文件来实现,总不能下次增加一个路径都要修改代码吧。于是被设计了一下配置文件如下:

spring.gateway.route.names=baidu,bing  ##表示有两个规则 baidu以及bing

spring.gateway.route.appender.baidu.rule=path   ##按路径
spring.gateway.route.appender.baidu.paths=/baidu/**,/baidu   ##哪些路径
spring.gateway.route.appender.baidu.forwards=https://www.baidu.com,https://www.baidu.com  ##指向的目标,可以填写多个,好实现负载均衡


spring.gateway.route.appender.bing.rule=path
spring.gateway.route.appender.bing.paths=/bing/**,/bing
spring.gateway.route.appender.bing.forwards=https://www.bing.com,https://www.bing.com

但很快就会发现 这一段代码添加的是一个类名不是对象,如果要添加一个规则,就为之新增一个类,那和硬代码也没啥区别了

list.add(new LoadBalancerClientSpecification("baidu", new Class[]{BaiduServiceInstanceListSupplier.class})); //百度

那么要实现动态配置,就需要动态生成类,这时就需要用到 groovy包了,虽然网上教动态生成类的教程很多,但groovy用起来更简单

public class ServiceInstanceListSupplierUtil {
    public static Class<?> getServiceInstanceListSupplierSimple(String name, List<Forward> forwards) {
        @SuppressWarnings("resource")
        GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
        String className = "ServiceInstanceListSupplier" + RandomStringUtils.randomAlphabetic(8) + System.currentTimeMillis() % 10000;
        try {
           //用字符串拼接出类
            String source = "package com.coke.util;\r\n"
                    + "\r\n"
                    + "import java.util.ArrayList;\r\n"
                    + "import java.util.List;\r\n"
                    + "\r\n"
                    + "import org.springframework.cloud.client.DefaultServiceInstance;\r\n"
                    + "import org.springframework.cloud.client.ServiceInstance;\r\n"
                    + "import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;\r\n"
                    + "\r\n"
                    + "import reactor.core.publisher.Flux;\r\n"
                    + "\r\n"
                    + "public class " + className + " implements ServiceInstanceListSupplier {\r\n"
                    + "\r\n"
                    + "  @Override\r\n"
                    + "   public Flux<List<ServiceInstance>> get() {\r\n"
                    + "     List<ServiceInstance> list = new ArrayList<ServiceInstance>();\r\n";


            for (Forward forward : forwards) {
                source += "     list.add(new DefaultServiceInstance(this.getServiceId() + list.size(), this.getServiceId(), "" + forward.getHost() + "", " + forward.getPort() + ", " + forward.isSecure() + "));\r\n";
            }

            source += "     return Flux.just(list);\r\n"
                    + "   }\r\n"
                    + "\r\n"
                    + " @Override\r\n"
                    + " public String getServiceId() {\r\n"
                    + "    return "" + name + "";\r\n"
                    + " }\r\n"
                    + "}";
           //动态生成类
            return groovyClassLoader.parseClass(source);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

最后代码就变成了

ocatorBuilder builder) {
    loadRouteRuleMap();
    RouteLocatorBuilder.Builder routesBuilder = builder.routes();
    for (RouteRule routeRule : routeRuleMap.values()) {
        routesBuilder.route(routeRule.getName(),r->r.path(routeRule.getPaths()).uri(routeRule.getUri())) ;//配置路径
    }
   return routesBuilder.build();
}
@Bean
@Primary
public LoadBalancerClientFactory loadBalancerClientFactory(LoadBalancerClientsProperties properties) throws IOException {
    loadRouteRuleMap();
    LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory(properties);
    List<LoadBalancerClientSpecification> list = new ArrayList<>();
    for (RouteRule routeRule : routeRuleMap.values()) {
        List<Forward> forwardList = new ArrayList<>();
        String[] forwards = routeRule.getForward();
        for (String forward : forwards) {
            Forward ward = UrlUtil.getForwardForUrl(forward);
            forwardList.add(ward);
        }
        //添加动态生成类
        list.add(new LoadBalancerClientSpecification(routeRule.getName(), new Class[]{ServiceInstanceListSupplierUtil.getServiceInstanceListSupplierSimple(routeRule.getName(),forwardList)})); 
    }
    clientFactory.setConfigurations(list);
    return clientFactory;
}

如此,就可以使用 spring-cloud-gateway 再无注册中心的情况下,实现类ningx那样的反向代理和负载均衡

代码的例子从下面链接下载

链接:pan.baidu.com/s/1kdqiXNZp…

提取码:guaj