Spring Gateway locator 自动路由兼容context-path

背景

基于Spring Gateway和Eureka 结合的微服务开发方式,
如果使用自动路由解析,可以将微服务上的eureka服务ID当成路由的key,从而能够根据网关地址+服务ID实现服务的自动转发。
Spring Gateway这么配置即可:

spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true

例如一个微服务的spring.application.name=my-service,其中有一个接口地址为/api/api-test。 则通过http://网关地址:端口/my-service/api/api-test就可以转发到这个微服务上。

然而,因为某种原因,这个微服务加了server.servlet.context-path=/my-service。这样的话就不能直接是用自动路由了。而必须手动的添加路由策略:

spring:
    name: gateway
  cloud:
    gateway:
      enabled: true
      routes:
        - id: my-service
          uri: lb://my-service
          predicates:
            - Path=/my-service/**

这个带来的问题是每加一个服务,都得手动在配置文件里面加入路由,而且需要重启网关才能生效。

那有没有一种方法既能根据eureka自动配置路由,又能加入context-path呢?

思路

答案当然是有的,首先要知道为啥网关能自动的将/my-service/api/api-test自动的转发到对应服务上的/api/api-test地址,通过负载均衡找到一个服务节点这个暂且不讨论,主要是把/my-service/api/api-test替换成/api/api-test的过程,这个过程其实是在org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory这个类里面定义的:

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
	ServerHttpRequest req = exchange.getRequest();
	addOriginalRequestUrl(exchange, req.getURI());
	String path = req.getURI().getRawPath();
	String newPath = path.replaceAll(config.regexp, replacement);
	ServerHttpRequest request = req.mutate().path(newPath).build();
	exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());
	return chain.filter(exchange.mutate().request(request).build());
}

那这个类是在哪里初始化的呢?
在这里:org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration

public static List<FilterDefinition> initFilters() {
	ArrayList<FilterDefinition> definitions = new ArrayList<>();
	// add a filter that removes /serviceId by default
	FilterDefinition filter = new FilterDefinition();
    filter.setName(normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
	String regex = "'/' + serviceId + '/?(?.*)'";
	String replacement = "'/${remaining}'";
	filter.addArg(REGEXP_KEY, regex);
	filter.addArg(REPLACEMENT_KEY, replacement);
	definitions.add(filter);
	return definitions;
}

所以看到了SpringCloudGateway的自动路由注入了替换URL的一个过滤器,它负责把地址中的serviceId替换掉,而我们加的这个context-path正好就是servcieId,所以只要不启用这个过滤器,就不会替换了。

解决

spring:
  application:
    name: gateway
  cloud:
    gateway:
      enabled: true
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
          filters:

是的,只要加一个filters就可以了,默认是空列表,但是还有一个更好的选择是,加入一些默认的过滤器,比如PreserveHostHeader,它可以将原始请求的Header信息保留,所以最终的配置如下:

spring:
 application:
   name: gateway
 cloud:
   gateway:
     enabled: true
     discovery:
       locator:
         enabled: true
         lower-case-service-id: true
         filters:
           - PreserveHostHeader

你可能感兴趣的:(SpringCloud,spring,微服务,gateway)