You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud Gateway:如何在启动时动态设置路由URI

解决Spring Cloud Gateway指定路由启动时通过服务发现一次性替换URI的问题

需求回顾

  • 启动阶段一次性替换特定路由的URI,通过服务发现获取真实地址
  • 仅对标记过的路由生效(示例中uri设为https://no.op的路由),以路由id作为服务名查找实例
  • 避免使用请求过滤器,消除每次请求的额外检查开销
  • 不采用DiscoveryClient Route Definition Locator方案(全量生效、配置写法不友好)

方案一:自定义BeanPostProcessor处理RouteLocator

通过Bean后置处理器,在RouteLocator Bean初始化完成后,批量修改符合条件的路由URI,实现启动时一次性替换。

配置示例

spring:
  cloud:
    gateway:
      routes:
        - id: my-portal
          uri: https://no.op  # 标记需要通过服务发现替换的路由
          predicates:
            - Path=/portal/**
        - id: my-portal-admin
          uri: https://admin.example.com  # 无需替换的路由
          predicates:
            - Path=/admin/**

代码实现

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;

@Component
public class ServiceDiscoveryRouteProcessor implements BeanPostProcessor {

    private final DiscoveryClient discoveryClient;

    public ServiceDiscoveryRouteProcessor(DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof RouteLocator) {
            RouteLocator originalLocator = (RouteLocator) bean;
            return () -> originalLocator.getRoutes().map(this::replaceUriWithServiceInstance);
        }
        return bean;
    }

    private Route replaceUriWithServiceInstance(Route route) {
        // 匹配标记为需要替换的路由
        if ("https://no.op".equals(route.getUri().toString())) {
            String serviceId = route.getId();
            // 从服务发现获取实例,此处取第一个实例,可根据负载均衡需求调整
            return discoveryClient.getInstances(serviceId).stream()
                    .findFirst()
                    .map(instance -> Route.async()
                            .asyncPredicate(route.getAsyncPredicate())
                            .filters(route.getFilters())
                            .id(route.getId())
                            .order(route.getOrder())
                            .predicate(route.getPredicate())
                            .uri(instance.getUri())
                            .build())
                    .orElse(route); // 服务不存在时保留原URI,可按需修改逻辑
        }
        return route;
    }
}

方案二:自定义RouteDefinitionRepository

在路由定义加载到内存的阶段直接替换URI,更早完成处理,同样是一次性操作。

代码实现

import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

@Component
public class ServiceDiscoveryRouteRepo extends InMemoryRouteDefinitionRepository {

    private final DiscoveryClient discoveryClient;

    public ServiceDiscoveryRouteRepo(DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.map(this::replaceUriIfMarked)
                .flatMap(super::save);
    }

    private RouteDefinition replaceUriIfMarked(RouteDefinition routeDefinition) {
        if ("https://no.op".equals(routeDefinition.getUri().toString())) {
            String serviceId = routeDefinition.getId();
            discoveryClient.getInstances(serviceId).stream()
                    .findFirst()
                    .ifPresent(instance -> routeDefinition.setUri(instance.getUri()));
        }
        return routeDefinition;
    }
}

关键说明

  1. 服务实例选择:示例中取第一个可用实例,实际可结合LoadBalancerClient实现负载均衡逻辑,替换实例选择逻辑即可。
  2. 标记自定义:除了https://no.op,也可使用其他自定义标记(如uri: service-discovery://trigger),只需调整代码中的匹配条件。
  3. 容错处理:示例中服务不存在时保留原URI,可根据业务需求改为抛出启动异常或降级到默认地址。

内容的提问来源于stack exchange,提问作者Damo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 20:38:07