源码分析-SpringCloudGateway源码阅读-服务发现

源码解析-SpringCloudGateway
01 源码分析-SpringCloudGateway源码阅读准备
02 源码分析-SpringCloudGateway源码阅读-网关监控端点
03 源码分析-SpringCloudGateway源码阅读-网关配置类
04 源码分析-SpringCloudGateway源码阅读-服务发现
05 源码分析-SpringCloudGateway源码阅读-网关自定义事件
06 源码分析-SpringCloudGateway源码阅读-过滤器包类总览
07 源码分析-SpringCloudGateway源码阅读-过滤器-全局过滤器源码(一)
08 源码分析-SpringCloudGateway源码阅读-过滤器-全局过滤器源码(二)
09 源码分析-SpringCloudGateway源码阅读-过滤器-全局过滤器源码(三)
10 源码分析-SpringCloudGateway源码阅读-过滤器-路由过滤器源码解读
11 源码分析-SpringCloudGateway源码阅读-处理器源码

一 服务发现类

主要涉及到三个类如下:

1
2
3
4
5
6
7
8
9
.
└── org
└── springframework
└── cloud
└── gateway
├── discovery
│   ├── DiscoveryClientRouteDefinitionLocator.java 服务发现客户端路由定义定位器-路由初始化
│   ├── DiscoveryLocatorProperties.java 服务发现相关配置参数
│   └── GatewayDiscoveryClientAutoConfiguration.java 网关服务发现客户端自动装配类

二 类解析

DiscoveryLocatorProperties.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.gateway.discovery;

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.core.style.ToStringCreator;

/**
* 服务发现配置参数类
*/
@ConfigurationProperties("spring.cloud.gateway.discovery.locator")
public class DiscoveryLocatorProperties {

/**
* 启用DiscoveryClient网关集成的标志
*/
/** Flag that enables DiscoveryClient gateway integration. */
private boolean enabled = false;

/**
* routeId的前缀,如果有配置前缀如 test1111_,则routeId为: test1111_service1
* 没配置时 默认为 DiscoveryClient.getClass().getSimpleName() + "_". 服务ID将被添加以创建routeId, 如: ReactiveCompositeDiscoveryClient_service1
*/
/**
* The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName()
* + "_". Service Id will be appended to create the routeId.
*/
private String routeIdPrefix;

/**
* 将评估是否在网关集成中包括服务的SpEL表达式,默认为:true。
*/
/**
* SpEL expression that will evaluate whether to include a service in gateway
* integration or not, defaults to: true.
*/
private String includeExpression = "true";

/**
* 为每个路由创建uri的SpEL表达式,默认为:'lb://'+serviceId
*/
/**
* SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId.
*/
private String urlExpression = "'lb://'+serviceId";

/**
* 谓词和过滤器中使serviceId小写,默认为false。
* 当eureka自动将serviceId大写时,此方法很有用。 因此 MYSERIVCE 将与 /myservice/** 匹配
*/
/**
* Option to lower case serviceId in predicates and filters, defaults to false. Useful
* with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match
* /myservice/**
*/
private boolean lowerCaseServiceId = false;

/**
* 谓词列表
*/
private List<PredicateDefinition> predicates = new ArrayList<>();

/**
* 过滤器
*/
private List<FilterDefinition> filters = new ArrayList<>();

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public String getRouteIdPrefix() {
return routeIdPrefix;
}

public void setRouteIdPrefix(String routeIdPrefix) {
this.routeIdPrefix = routeIdPrefix;
}

public String getIncludeExpression() {
return includeExpression;
}

public void setIncludeExpression(String includeExpression) {
this.includeExpression = includeExpression;
}

public String getUrlExpression() {
return urlExpression;
}

public void setUrlExpression(String urlExpression) {
this.urlExpression = urlExpression;
}

public boolean isLowerCaseServiceId() {
return lowerCaseServiceId;
}

public void setLowerCaseServiceId(boolean lowerCaseServiceId) {
this.lowerCaseServiceId = lowerCaseServiceId;
}

public List<PredicateDefinition> getPredicates() {
return predicates;
}

public void setPredicates(List<PredicateDefinition> predicates) {
this.predicates = predicates;
}

public List<FilterDefinition> getFilters() {
return filters;
}

public void setFilters(List<FilterDefinition> filters) {
this.filters = filters;
}

@Override
public String toString() {
return new ToStringCreator(this).append("enabled", enabled)
.append("routeIdPrefix", routeIdPrefix)
.append("includeExpression", includeExpression)
.append("urlExpression", urlExpression)
.append("lowerCaseServiceId", lowerCaseServiceId)
.append("predicates", predicates).append("filters", filters).toString();
}

}

DiscoveryClientRouteDefinitionLocator.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.gateway.discovery;

import java.net.URI;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.core.style.ToStringCreator;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.util.StringUtils;

/**
* 服务发现客户端路由定义定位器
* TODO: change to RouteLocator? use java dsl
*
* @author Spencer Gibb
*/
public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {

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

/**
* 配置
*/
private final DiscoveryLocatorProperties properties;

/**
* 路由id前缀
*/
private final String routeIdPrefix;

/**
* SimpleEvaluationContext - 针对不需要SpEL语言语法的全部范围并且应该受到有意限制的表达式类别,公开Spal语言特性和配置选项的子集。示例包括但不限于数据绑定表达式,基于属性的过滤器等
*/
private final SimpleEvaluationContext evalCtxt;

private Flux<List<ServiceInstance>> serviceInstances;

/**
* Kept for backwards compatibility. You should use the reactive discovery client.
* @param discoveryClient the blocking discovery client 阻塞的服务发现客户端
* @param properties the configuration properties 配置属性
* @deprecated kept for backwards compatibility
*/
@Deprecated
public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient,
DiscoveryLocatorProperties properties) {
this(discoveryClient.getClass().getSimpleName(), properties);
serviceInstances = Flux
.defer(() -> Flux.fromIterable(discoveryClient.getServices()))
.map(discoveryClient::getInstances)
.subscribeOn(Schedulers.boundedElastic());
}

/**
* @param discoveryClient 反应式服务发现客户端
* @param properties 配置属性
*/
public DiscoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient,
DiscoveryLocatorProperties properties) {
this(discoveryClient.getClass().getSimpleName(), properties);
serviceInstances = discoveryClient.getServices()
.flatMap(service -> discoveryClient.getInstances(service).collectList());
}

/**
*
* @param discoveryClientName 服务发现客户端类名 simpleName
* @param properties 配置属性
*/
private DiscoveryClientRouteDefinitionLocator(String discoveryClientName,
DiscoveryLocatorProperties properties) {
this.properties = properties;
// 如果有配置路由id前缀
if (StringUtils.hasText(properties.getRouteIdPrefix())) {
routeIdPrefix = properties.getRouteIdPrefix();
}
// 没配置路由id前缀则使用 discoveryClientName + "_" 作为路由id前缀
else {
routeIdPrefix = discoveryClientName + "_";
}
evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods()
.build();
}

/**
* 获取路由定义
* @return
*/
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
// SpEL语言解析器
SpelExpressionParser parser = new SpelExpressionParser();
// 是否包含el表达式的el表达式
Expression includeExpr = parser
.parseExpression(properties.getIncludeExpression());
// url的el表达式
Expression urlExpr = parser.parseExpression(properties.getUrlExpression());

Predicate<ServiceInstance> includePredicate;
// includeExpression 如果为空 或者 为 true字符串 则为true
if (properties.getIncludeExpression() == null
|| "true".equalsIgnoreCase(properties.getIncludeExpression())) {
includePredicate = instance -> true;
}
else {
includePredicate = instance -> {
Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);
if (include == null) {
return false;
}
return include;
};
}

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()) {
// 根据表达式 获取到谓词value的字符串
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()) {
// 根据表达式 获取到过滤器value的字符串
String value = getValueFromExpr(evalCtxt, parser,
instanceForEval, entry);
filter.addArg(entry.getKey(), value);
}
routeDefinition.getFilters().add(filter);
}

return routeDefinition;
});
}

/**
* build路由定义
* @param urlExpr
* @param serviceInstance
* @return
*/
protected RouteDefinition buildRouteDefinition(Expression urlExpr,
ServiceInstance serviceInstance) {
String serviceId = serviceInstance.getServiceId();
RouteDefinition routeDefinition = new RouteDefinition();
// 设置路由id
routeDefinition.setId(this.routeIdPrefix + serviceId);
String uri = urlExpr.getValue(this.evalCtxt, serviceInstance, String.class);
// 设置路由uri
routeDefinition.setUri(URI.create(uri));
// add instance metadata
routeDefinition.setMetadata(new LinkedHashMap<>(serviceInstance.getMetadata()));
return routeDefinition;
}

/**
* 根据表达式过滤el表达式的值
* @param evalCtxt
* @param parser
* @param instance
* @param entry
* @return
*/
String getValueFromExpr(SimpleEvaluationContext evalCtxt, SpelExpressionParser parser,
ServiceInstance instance, Map.Entry<String, String> entry) {
try {
Expression valueExpr = parser.parseExpression(entry.getValue());
return valueExpr.getValue(evalCtxt, instance, String.class);
}
catch (ParseException | EvaluationException e) {
if (log.isDebugEnabled()) {
log.debug("Unable to parse " + entry.getValue(), e);
}
throw e;
}
}

/**
* 服务实例 委托delegate模式
*/
private static class DelegatingServiceInstance implements ServiceInstance {

final ServiceInstance delegate;

private final DiscoveryLocatorProperties properties;

private DelegatingServiceInstance(ServiceInstance delegate,
DiscoveryLocatorProperties properties) {
this.delegate = delegate;
this.properties = properties;
}

@Override
public String getServiceId() {
if (properties.isLowerCaseServiceId()) {
return delegate.getServiceId().toLowerCase();
}
return delegate.getServiceId();
}

@Override
public String getHost() {
return delegate.getHost();
}

@Override
public int getPort() {
return delegate.getPort();
}

@Override
public boolean isSecure() {
return delegate.isSecure();
}

@Override
public URI getUri() {
return delegate.getUri();
}

@Override
public Map<String, String> getMetadata() {
return delegate.getMetadata();
}

@Override
public String getScheme() {
return delegate.getScheme();
}

@Override
public String toString() {
return new ToStringCreator(this).append("delegate", delegate)
.append("properties", properties).toString();
}

}

}

GatewayDiscoveryClientAutoConfiguration.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.gateway.discovery;

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.DispatcherHandler;

import static org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory.REGEXP_KEY;
import static org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory.REPLACEMENT_KEY;
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory.PATTERN_KEY;
import static org.springframework.cloud.gateway.support.NameUtils.normalizeFilterFactoryName;
import static org.springframework.cloud.gateway.support.NameUtils.normalizeRoutePredicateName;

/**
* 网关服务发现客户端自动装配类
* @author Spencer Gibb
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@AutoConfigureBefore(GatewayAutoConfiguration.class)
@AutoConfigureAfter(CompositeDiscoveryClientAutoConfiguration.class)
@ConditionalOnClass({ DispatcherHandler.class })
@EnableConfigurationProperties
public class GatewayDiscoveryClientAutoConfiguration {

/**
* 初始化一个默认的谓词定义
* @return
*/
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;
}

/**
* 初始化一个默认的过滤器
* @return
*/
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
* @return
*/
@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
* @param discoveryClient
* @param properties
* @return
*/
@Bean
@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(
ReactiveDiscoveryClient discoveryClient,
DiscoveryLocatorProperties properties) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
}

}

/**
* @deprecated In favor of the native reactive service discovery capability.
*/
@Configuration(proxyBeanMethods = false)
@Deprecated
@ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled",
havingValue = "false")
public static class BlockingDiscoveryClientRouteDefinitionLocatorConfiguration {

/**
* 阻塞的服务发现客户端路由定义定位器
* @param discoveryClient
* @param properties
* @return
*/
@Bean
@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(
DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
}

}

}