按照服务名动态路由

如果按照我们上面的理解,所有的路由在程序中都对应一个RouteDefenition对象,下面我们来看看gateway是如何根据服务名来生成对应的路由对象RouteDefenition的?

打开GatewayDiscoveryClientAutoConfiguration,根据我们的配置会生成一个DiscoveryClientRouteDefinitionLocator对象bean。

下面我们分析一下DiscoveryClientRouteDefinitionLocator的构造方法和getRouteDefinitions方法,看看是如何生成RouteDefinition的。

在构造方法中,如上图所示,调用了ReactiveDiscoveryClient.getSerivce方法,采用的是响应式编程,返回一个Flux<List<ServiceInstance>>流对象,流对象内部的元素为注册中心的服务实例。

下面我们再看getRouteDefinitions方法,

该方法会遍历所有服务实例,为每个服务生成一个路由。如上图所示

设置uri

在buildRouteDefinition中,new了一个RouteDefinition,并设置了路由的id属性为固定前缀+serviceId,并设置了uri属性为lb://serviceId,如下图所示:

protected RouteDefinition buildRouteDefinition(Expression urlExpr,
			ServiceInstance serviceInstance) {
		String serviceId = serviceInstance.getServiceId();
		RouteDefinition routeDefinition = new RouteDefinition();
		routeDefinition.setId(this.routeIdPrefix + serviceId);
		String uri = urlExpr.getValue(this.evalCtxt, serviceInstance, String.class);
		routeDefinition.setUri(URI.create(uri));
		// add instance metadata
		routeDefinition.setMetadata(new LinkedHashMap<>(serviceInstance.getMetadata()));
		return routeDefinition;
	}

设置断言Predicate

紧接着,遍历了配置类里的Predicate,为路由设置了断言,如下图所示:

 

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);
}

这里注意到断言是从this.properties.getPredicates()取得,但是我们没有在配置文件中配置断言,是不是意味着取得的值为空呢,其实不然,

继续打开GatewayDiscoveryClientAutoConfiguration,我们可以看到类里显式创建了一个DiscoveryLocatorProperties,并通过调用initPredicates设置了断言,调用initFilters方法设置了过滤器,如下图所示:

可以分析出使用的断言为PathRoutePredicateFactory,并设置了pattern为 /serviceId/**,跟我们在配置文件中配置成 -Path=xxx是一样的,在上面给routeDefenition设置断言时是会将serviceId替换成具体的服务名。至此,断言设置完成。

	@Bean
	public DiscoveryLocatorProperties discoveryLocatorProperties() {
		DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
		properties.setPredicates(initPredicates());
		properties.setFilters(initFilters());
		return properties;
	}



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;
	}

	

设置filter

设置filter的代码如下,也是从this.properties.getFilters()中取得。

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);
					}

在上面我们分析断言时,看到同时通过initFilters方法给properties设置了filters属性,使用的是RewritePathGatewayFilterFactory,同时指定了重写的规则,即会把路径中的服务名去掉,然后再请求到下游服务。如下图所示:

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;
	}

至此,filter设置完成。

我们可以通过gateway暴露的endpoint来查看所有的路由信息,如下图:

​​

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐