虽然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那样的反向代理和负载均衡
代码的例子从下面链接下载
提取码:guaj