2019 年的一个 Spring Cloud Gateway 工程,把路由 JSON 放进 Nacos,监听变更后转成 RouteDefinition,再交给 RouteDefinitionWriter。这样可以改变路由而不重新打包,但更新过程必须与网关的可用性一起设计。

以下围绕旧版响应式 Gateway API 解释,不是可以直接复制上线的完整组件。

路由 JSON 怎样对应转发规则

一个路由定义可以写成:

[
  {
    "id": "example-consumer",
    "order": 0,
    "uri": "lb://consumer",
    "predicates": [
      { "name": "Path", "args": { "pattern": "/consumer/**" } }
    ],
    "filters": [
      { "name": "StripPrefix", "args": { "parts": "1" } }
    ]
  }
]

Path 匹配入口路径,StripPrefix=1 去掉一个路径段;例如 /consumer/name 经过该过滤器变成 /name。lb://consumer 需要匹配版本的服务发现与负载均衡支持,不是普通的主机名替换。Gateway 2.2.9 参考

Nacos 中的 namespace、data ID 和 group 要与客户端一致。JSON 格式正确,也不代表路由目标安全或一定存在。

初始配置和后续变更都要进入同一流程

客户端获取初始文本、注册 Listener 后,两个入口都应进入统一的解析、校验和应用流程,不能初始 getConfig 只打印,等用户下一次发布才真正装载路由。Nacos Java SDK

拉取与监听之间还可能有竞态,因此应按所用 SDK 设计一致性检查,携带配置版本或内容摘要,串行处理重复和乱序变更。

不要先清空,再逐个异步保存

先删全部路由再解析,遇到空配置、解析失败或保存失败时可能失去正常路由。对每条操作分别 subscribe,也不能保证全部完成后才刷新。

对已经解析、验证并计算差异的配置,可以在一个响应式链里串联操作。下面的方法仅示意时序,writer 与 publisher 为已有组件:

Mono<Void> applyValidatedChanges(
        List<RouteDefinition> upserts, List<String> obsoleteIds) {
    return Flux.fromIterable(upserts)
        .concatMap(route -> writer.save(Mono.just(route)))
        .thenMany(Flux.fromIterable(obsoleteIds)
            .concatMap(id -> writer.delete(Mono.just(id))))
        .then(Mono.fromRunnable(() ->
            publisher.publishEvent(new RefreshRoutesEvent(this))));
}

List、Flux、Mono 及路由类型需来自匹配依赖。调用方必须订阅或返回该链,并统一处理错误;只构造 Mono 不会自动执行。

这个链保证的是所示操作的先后,不是事务或自动回滚。中途失败仍可能部分生效,需要保存上一个可用版本、记录结果并设计恢复。只管理本组件拥有的动态路由 ID,不能顺手删除静态或其他来源路由。

配置也属于高权限输入

应用前检查空值策略、重复 ID、谓词、过滤器、URI 协议与允许的目标;拒绝无效版本时保留可用配置。多实例还要观测各自应用版本,而不是只看到 Nacos 发布成功。

动态路由应限制配置编辑权限和可转发目标,避免成为越权访问内部服务的入口。刷新事件只是流程的一环,不替代配置校验、失败恢复和实际转发验证。