r有时我们用springcloudgateway做网关的时候,会发现调用的url少了一层,这个是什么做到的呢,我们就根据源码分析一下。
一、初始化
在GatewayDiscoveryClientAutoConfiguration类中创建了bean:DiscoveryLocatorProperties
@Bean
public DiscoveryLocatorProperties discoveryLocatorProperties() {
DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
properties.setPredicates(initPredicates());
properties.setFilters(initFilters());
return properties;
}
上面代码中有一行方法:properties.setFilters(initFilters()),返回了一个FilterDefinition
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;
}
当我们一个请求经过网关的时候,我们会经过getRouteDefinitions方法,这个方法中里面有这么一块代码,就是把注册到zookeeper上面的applicationName放到了filter中:
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);
}
其中的appName等信息,是从别的服务注册到zookeeper上获取的,具体内容是从instance中取到的,instantce从zookeeper获取的信息内容如下:
ServiceInstance{name='upload', id='172.11.6.174', address='172.11.6.174', port=8016, sslPort=null, payload=ZookeeperInstance{id='application-1', name='upload', metadata={}}, registrationTimeUTC=1530273446489, serviceType=DYNAMIC, uriSpec=org.apache.curator.x.discovery.UriSpec@6c2ac0dc, enabled=true}
关于获取zookeeper的instance我们放到另一篇文章里面。
处理完后我们就放到了routeDifinition中返回,此时serviceId就放在了routDefinition中。
然后在RouteDefinitionRouteLocator中的loadGatewayFilters方法中,执行了GatewayFilterFactory的apply()方法,实际上用的是RewritePathGatewayFilterFactory,入参的Config就是RewritePathGatewayFilterFactory的内部类Config
@Override
public GatewayFilter apply(Config config) {
String replacement = config.replacement.replace("$\\", "$");
return (exchange, 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());
};
}
在这段apply方法中,regexp为:/application/(?<remaining>.*) replacement为:/${remaining},然后将获取的newPath放到了Request上,并且放入了GATEWAY_REQUEST_URL_ATTR属性,后面请求就是根据rewrite中处理的新的path进行请求了