https://www.codenong.com/jsf04a503f0d32/

 

 

spring cloud gateway 二次开发之 动态路由注意事项

 

网关开发的过程中,因为有对某些服务进行动态的上下线的需求,所以进行了动态路由的开发,网上也有例子,实现方式就不赘述了,但这里有2个注意事项。

 

一 路由信息里的断言器信息不能为空

如果没有传断言器信息,比如这样

 

1
2
3
4
5
6
7
{
    "id":"test",
    "predicates":[],
    "filters":[],
    "uri":"lb://test",
    "order":0
}

会抛出异常

 

1
2
3
4
5
6
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中第一个断言器,然后与之后的断言器做组合,

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 的断言器名字

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
    "id":"test",
    "predicates":[
        {
            "name":"zuibuxing",
            "args":{
                "_genkey_0":"/test/**"
            }
        }
    ],
    "filters":[

    ],
    "uri":"lb://test",
    "order":0
}

会报这个错误

 

1
2
3
4
5
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 里去寻找,

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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());
        }

        ...以下代码省略
    }
1
 

 

 

RouteDefinitionRouteLocator 在这个Map中保存所有实现了 RoutePredicateFactory 的类,name是类名去掉RoutePredicateFactory,比如 AfterRoutePredicateFactory 断言器的名字是 After,这里有两个特殊的断言器 CloudFoundryRouteService,ReadBodyPredicateFactory,因为它们的类名没有以 RoutePredicateFactory 结尾,所以就用本身类名作为 断言器name
如果是随便写的,会导致找不到断言器报错,所以我们只能填gateway自己的断言器,或者自己实现了 RoutePredicateFactory 的断言器