动态路由修改

当我们看了Spring Cloud Gateway的示例代码,和一些网上的入门教程之后,会发现大多是Spring Cloud Gateway — 网关基本功能API暴露说道的三种方法:1.本地配置文件;2.java代码builder.routes().route().build(); 3.服务自动发现。

服务自动发现方法能够做到后端服务ip变化时自动更新,能够做到上游服务的动态。另外两种方式都是纯静态,需要重新启动网关服务修改才能生效。

上述方式都不能完全满足生产需要,一般来说作为生产上的通用网关要求有路由动态修改能力,不重启网关服务即可即使生效。无论是新增路由、或者是修改路由的过滤规则,添加过滤器都可以不重启网关生效。

内置的动态路由

Spring Cloud Gateway内置了一个动态路由类InMemoryRouteDefinitionRepository。这是一个使用内存存储路由的类,支持acurator接口对路由进行增删改查,但是当网关重启之后所有的内存路由都会消失,也就是说它没有持久化能力。

虽然InMemoryRouteDefinitionRepository没有达到生产级动态路由修改的需求,但是它提供了一种思路,起到了示例作用。如果需要实现自己的可持久化动态路由,可以仿照InMemoryRouteDefinitionRepository的路由解析加载能力,以及更新方式实现在即的动态路由。

基于分布式配置的动态路由

基于分布式配置可能是一种最简单便捷的路由动态修改能力了,这里展示基于spring cloud config实现Spring Cloud Gateway动态路由修改。

/**
 * a route definition locator, that locate route definition from remote config server or local properties file.
 * route definition is json value, not yaml value, e.g.
 * "route-definitions=[{\"id\": \"websocket_test\",\"uri\": \"ws://localhost:9000\",\"order\": 9000,\"predicates\":[\"Path=/echo\"],\"filters\":[\"AddRequestHeader=x-tt-token, 123456\"]}]"
 * or
 * "route-definitions=[{\"id\": \"websocket_test\",\"uri\": \"ws://localhost:9000\",\"order\": 9000,\"predicates\":[{\"name\":\"Path\", \"args\":{\"_genkey_0\":\"/echo\"}}],\"filters\":[{\"name\":\"AddRequestHeader\", \"args\":{\"_genkey_0\":\"x-tt-token\",\"_genkey_1\":\"123456\"}}]}]"
 *
 * compare to spring cloud gateway's default InMemoryRouteDefinitionRepository, route definitions could be loaded when program's startup.
 *
 * to do this, there is another way. define a bean and load remote route definitions to InMemoryRouteDefinitionRepository when program's startup.
 * otherwise, you should implement ApplicationListener<ApplicationEvent>. when refresh event emited, remote route definitions should be reloaded
 * into InMemoryRouteDefinitionRepository.
 */
public class ConfigServerRouteDefinitionLocator implements RouteDefinitionLocator {
    @Value("${route-definitions}")
    private String routeDefinitions;//a json string, contains route definition lists

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        ObjectMapper mapper = new ObjectMapper();
        List<RouteDefinition> rdList = null;
        try {
            System.out.println(routeDefinitions);
            rdList = mapper.readValue(routeDefinitions, new TypeReference<List<RouteDefinition>>() { });
            return Flux.fromArray(rdList.toArray(new RouteDefinition[]{}));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        return Flux.empty();
    }
}

方法很简单,只需要在config server里增加一个路由的配置项即可。config server可以调用Spring Cloud Gateway的路由更新接口/refresh,路由配置就会自动更新。

如果要实现更复杂的控制逻辑,比如路由配置的路由校验、版本控制、灰度发布等其他功能,可以在定义自己的refresh接口和管理后台进行控制。

其他方式

如果时间允许,可以使用其他存储方式实现路由动态修改能力。可以参考以下文章:

基于Redis实现动态路由修改