网关开发的过程中,因为有对某些服务进行动态的上下线的需求,所以进行了动态路由的开发,网上也有例子,实现方式就不赘述了,但这里有2个注意事项。
这两个错误很严重,一旦出现了之后,和路由有关的功能都无法再使用,需要重启项目
一 路由信息里的断言器信息不能为空
如果在添加路由信息的时候没有传断言器,比如这样
{
"id":"test",
"predicates":[],
"filters":[],
"uri":"lb://test",
"order":0
}
会抛出异常
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
Caused by: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.get(ArrayList.java:429)
at org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator.combinePredicates(RouteDefinitionRouteLocator.java:221)
at org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator.convertToRoute(RouteDefinitionRouteLocator.java:143)
我们看路由信息类的时候,可以发现断言器是一个List,RouteDefinitionRouteLocator下边有这个方法,会先取List中第一个断言器,然后与之后的断言器做组合,
private AsyncPredicate<ServerWebExchange> combinePredicates(
RouteDefinition routeDefinition) {
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
//取出第一个断言器
AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
predicates.get(0));
//取出后续断言器
for (PredicateDefinition andPredicate : predicates.subList(1,
predicates.size())) {
AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
andPredicate);
//第一个断言器和后续断言器做 and 操作
predicate = predicate.and(found);
}
return predicate;
}
如果没有填断言器信息,就会报数组越界异常
二 断言器的名字不能随便取
比如这里我取了一个 zuibuxing 的断言器名字
{
"id":"test",
"predicates":[
{
"name":"zuibuxing",
"args":{
"_genkey_0":"/test/**"
}
}
],
"filters":[
],
"uri":"lb://test",
"order":0
}
会报这个错误
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: Unable to find RoutePredicateFactory with name zuibuxing
Caused by: java.lang.IllegalArgumentException: Unable to find RoutePredicateFactory with name zuibuxing
at org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator.lookup(RouteDefinitionRouteLocator.java:240)
at org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator.combinePredicates(RouteDefinitionRouteLocator.java:220)
at org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator.convertToRoute(RouteDefinitionRouteLocator.java:143)
说找不到名字为 zuibuxing 的断言器,这是因为保存自定义路由的时候,RouteDefinitionRouteLocato 会按照 name 去断言器Map 里去寻找,
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
... 中间代码省略 ....
//按照名字寻找断言器
private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route,
PredicateDefinition predicate) {
RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
if (factory == null) {
throw new IllegalArgumentException(
"Unable to find RoutePredicateFactory with name "
+ predicate.getName());
}
...以下代码省略
}
RouteDefinitionRouteLocator 在这个Map中保存所有实现了 RoutePredicateFactory 的类,name是类名去掉RoutePredicateFactory,比如 AfterRoutePredicateFactory 断言器的名字是 After,这里有两个特殊的断言器 CloudFoundryRouteService,ReadBodyPredicateFactory,因为它们的类名没有以 RoutePredicateFactory 结尾,所以就用本身类名作为 断言器name
如果是随便写的,会导致找不到断言器报错,所以我们只能填gateway自己的断言器,或者自己实现了 RoutePredicateFactory 的断言器