前言

前一阵业务需求所以自定义了一个GatewayFilter,然后发现在我开启对应服务的时候,它不经过我自定义的GatewayFilter,在我停掉对应服务后,就会进入自定义的GatewayFilter。几经研究发现了问题,特此记录。

一、示例

自定义的GatewayFilter
PreviewGatewayFilterFactory.java


/**
 * @author QLZ
 * @date 2021/6/29
 */
@Component
@Slf4j
public class PreviewGatewayFilterFactory extends AbstractGatewayFilterFactory<PreviewGatewayFilterFactory.Config> {
    public PreviewGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Lists.newArrayList("enabled");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            log.info("每次路由匹配到会执行");
            Boolean enabled = config.getEnabled();
            log.info("从配置文件中获取到的 enabled 的值=[{}].", enabled);
            return chain.filter(exchange);
        };
    }
    @Data
    public static class Config {
        private Boolean enabled;
    }

}

在路由中设置filters

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true #使用小写service-id
      routes: #配置路由路径
 		- id: wallpaper-portal
          uri: lb://wallpaper-portal
          predicates:
            - Path=/wallpaper-portal/**
          filters:
            - Preview=true
            - StripPrefix=1

二、问题出在哪?

正常情况下按照上述代码编写和配置后,请求对应的URL就会进入自定义的PreviewGatewayFilterFactory 过滤器,而没有进入的原因是因为开启了

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

开启此配置表示,gateway可以在注册中心(如:nacos)中根据服务id自动创建路由规则。而不经过我们手动配置的路由规则。换句话说就是开启了这个功能,实际上就可以不用在配置文件里写routes配置路由路径了。而带来的问题就是,它无法适配我们自己写的filter和predicate。
源码:
GatewayDiscoveryClientAutoConfiguration.java

public class GatewayDiscoveryClientAutoConfiguration {

	public static List<PredicateDefinition> initPredicates() {
		ArrayList<PredicateDefinition> definitions = new ArrayList<>();
		// TODO: add a predicate that matches the url at /serviceId?

		// add a predicate that matches the url at /serviceId/**
		PredicateDefinition predicate = new PredicateDefinition();
		predicate.setName(normalizeRoutePredicateName(PathRoutePredicateFactory.class));
		predicate.addArg(PATTERN_KEY, "'/'+serviceId+'/**'");
		definitions.add(predicate);
		return definitions;
	}

	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 + '/(?<remaining>.*)'";
		String replacement = "'/${remaining}'";
		filter.addArg(REGEXP_KEY, regex);
		filter.addArg(REPLACEMENT_KEY, replacement);
		definitions.add(filter);

		return definitions;
	}
	@Bean
	public DiscoveryLocatorProperties discoveryLocatorProperties() {
		DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
		properties.setPredicates(initPredicates());
		properties.setFilters(initFilters());
		return properties;
	}
	....//省略若干代码
	
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled",
			matchIfMissing = true)
	public static class ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration {

		@Bean
		//如果为true则创建此bean
		@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
		public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(
				ReactiveDiscoveryClient discoveryClient,
				DiscoveryLocatorProperties properties) {
			return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
		}

	}
}

DiscoveryClientRouteDefinitionLocator.java


public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {

	private static final Log log = LogFactory
			.getLog(DiscoveryClientRouteDefinitionLocator.class);

	private final DiscoveryLocatorProperties properties;

	private final String routeIdPrefix;

	private final SimpleEvaluationContext evalCtxt;

	private Flux<List<ServiceInstance>> serviceInstances;



	public DiscoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient,
			DiscoveryLocatorProperties properties) {
		this(discoveryClient.getClass().getSimpleName(), properties);
		serviceInstances = discoveryClient.getServices()
				.flatMap(service -> discoveryClient.getInstances(service).collectList());
	}

	private DiscoveryClientRouteDefinitionLocator(String discoveryClientName,
			DiscoveryLocatorProperties properties) {
		this.properties = properties;
		if (StringUtils.hasText(properties.getRouteIdPrefix())) {
			routeIdPrefix = properties.getRouteIdPrefix();
		}
		else {
			routeIdPrefix = discoveryClientName + "_";
		}
		evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods()
				.build();
	}

	@Override
	public Flux<RouteDefinition> getRouteDefinitions() {

		...//省略若干代码

		return serviceInstances.filter(instances -> !instances.isEmpty())
				.map(instances -> instances.get(0)).filter(includePredicate)
				.map(instance -> {
					RouteDefinition routeDefinition = buildRouteDefinition(urlExpr,
							instance);

					final ServiceInstance instanceForEval = new DelegatingServiceInstance(
							instance, properties);
				//设置断言
					for (PredicateDefinition original : this.properties.getPredicates()) {
						PredicateDefinition predicate = new PredicateDefinition();
						predicate.setName(original.getName());
						for (Map.Entry<String, String> entry : original.getArgs()
								.entrySet()) {
							String value = getValueFromExpr(evalCtxt, parser,
									instanceForEval, entry);
							predicate.addArg(entry.getKey(), value);
						}
						routeDefinition.getPredicates().add(predicate);
					}
					//设置过滤器
					for (FilterDefinition original : this.properties.getFilters()) {
						FilterDefinition filter = new FilterDefinition();
						filter.setName(original.getName());
						for (Map.Entry<String, String> entry : original.getArgs()
								.entrySet()) {
							String value = getValueFromExpr(evalCtxt, parser,
									instanceForEval, entry);
							filter.addArg(entry.getKey(), value);
						}
						routeDefinition.getFilters().add(filter);
					}

					return routeDefinition;
				});
	}

}

看源码可知,开启spring.cloud.gateway.discovery.locator.enabled=true后,创建
DiscoveryClientRouteDefinitionLocator对象,此对象仅包含一个PathRoutePredicateFactory.class类型的Predicate 和 RewritePathGatewayFilterFactory.class类型的filter。并不会包含进自定义的filter。所以才会无效。

总结

spring.cloud.gateway.discovery.locator.enabled=false后,自定义的gateway filter才会生效。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐