源码分析-SpringCloudGateway源码阅读-网关监控端点

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

一 监控端点说明

监控端点,是为了方便管理和监视运行中的服务,默认情况下,这些监控端点都是未初始化的。

SCG网关需要添加以下配置启用端点:

1
2
management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway

spring cloud gateway网关的监控端点一共涉及到三个类如下

  • org.springframework.cloud.gateway.actuate.AbstractGatewayControllerEndpoint
  • org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint 新实现类 默认使用该实现类
  • org.springframework.cloud.gateway.actuate.GatewayLegacyControllerEndpoint 旧实现类 默认不会使用,仅当配置了 spring.cloud.gateway.actuator.verbose.enabled=false才会使用这个实现,而不使用新的实现

三个类的关系如图:

image-20210126180040727

二 源码阅读之代码阅读

AbstractGatewayControllerEndpoint抽象类如下:

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
/*
* 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.actuate;

import java.net.URI;
import java.util.HashMap;
import java.util.List;

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

import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.Ordered;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

/**
* 网关端点抽象类
*
* @author Spencer Gibb
*/
public class AbstractGatewayControllerEndpoint implements ApplicationEventPublisherAware {

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

protected RouteDefinitionLocator routeDefinitionLocator;

/**
* 全局过滤器
*/
protected List<GlobalFilter> globalFilters;

/**
* 路由过滤器
*/
// TODO change casing in next major release
protected List<GatewayFilterFactory> GatewayFilters;

/**
* 谓词工厂
*/
protected List<RoutePredicateFactory> routePredicates;

/**
* 路由定义写入类
*/
protected RouteDefinitionWriter routeDefinitionWriter;


/**
* 路由定位器
*/
protected RouteLocator routeLocator;

protected ApplicationEventPublisher publisher;

public AbstractGatewayControllerEndpoint(
RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.globalFilters = globalFilters;
this.GatewayFilters = gatewayFilters;
this.routePredicates = routePredicates;
this.routeDefinitionWriter = routeDefinitionWriter;
this.routeLocator = routeLocator;
}

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}

// TODO: Add uncommited or new but not active routes endpoint

/**
* 触发刷新路由事件
* @return
*/
@PostMapping("/refresh")
public Mono<Void> refresh() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return Mono.empty();
}

/**
* 获取全局过滤器
* @return
*/
@GetMapping("/globalfilters")
public Mono<HashMap<String, Object>> globalfilters() {
return getNamesToOrders(this.globalFilters);
}

/**
* 获取路由过滤器
* @return
*/
@GetMapping("/routefilters")
public Mono<HashMap<String, Object>> routefilers() {
return getNamesToOrders(this.GatewayFilters);
}

/**
* 获取谓词工厂
* @return
*/
@GetMapping("/routepredicates")
public Mono<HashMap<String, Object>> routepredicates() {
return getNamesToOrders(this.routePredicates);
}

private <T> Mono<HashMap<String, Object>> getNamesToOrders(List<T> list) {
return Flux.fromIterable(list).reduce(new HashMap<>(), this::putItem);
}

/**
* 合并Object的方法
* @param map
* @param o
* @return 返回合并之后的类的顺序map
*/
private HashMap<String, Object> putItem(HashMap<String, Object> map, Object o) {
Integer order = null;
// 如果有继承 Ordered接口 则获取到order
if (o instanceof Ordered) {
order = ((Ordered) o).getOrder();
}
// filters.put(o.getClass().getName(), order);
map.put(o.toString(), order);
return map;
}

/*
* http POST :8080/admin/gateway/routes/apiaddreqhead uri=http://httpbin.org:80
* predicates:='["Host=**.apiaddrequestheader.org", "Path=/headers"]'
* filters:='["AddRequestHeader=X-Request-ApiFoo, ApiBar"]'
*/
@PostMapping("/routes/{id}")
@SuppressWarnings("unchecked")
public Mono<ResponseEntity<Object>> save(@PathVariable String id,
@RequestBody RouteDefinition route) {

return Mono.just(route).filter(this::validateRouteDefinition)
.flatMap(routeDefinition -> this.routeDefinitionWriter
.save(Mono.just(routeDefinition).map(r -> {
r.setId(id);
log.debug("Saving route: " + route);
return r;
}))
.then(Mono.defer(() -> Mono.just(ResponseEntity
.created(URI.create("/routes/" + id)).build()))))
.switchIfEmpty(
Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
}

private boolean validateRouteDefinition(RouteDefinition routeDefinition) {
boolean hasValidFilterDefinitions = routeDefinition.getFilters().stream()
.allMatch(filterDefinition -> GatewayFilters.stream()
.anyMatch(gatewayFilterFactory -> filterDefinition.getName()
.equals(gatewayFilterFactory.name())));

boolean hasValidPredicateDefinitions = routeDefinition.getPredicates().stream()
.allMatch(predicateDefinition -> routePredicates.stream()
.anyMatch(routePredicate -> predicateDefinition.getName()
.equals(routePredicate.name())));
log.debug("FilterDefinitions valid: " + hasValidFilterDefinitions);
log.debug("PredicateDefinitions valid: " + hasValidPredicateDefinitions);
return hasValidFilterDefinitions && hasValidPredicateDefinitions;
}

/**
* 根据ID删除路由
* @return
*/
@DeleteMapping("/routes/{id}")
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
return this.routeDefinitionWriter.delete(Mono.just(id))
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
.onErrorResume(t -> t instanceof NotFoundException,
t -> Mono.just(ResponseEntity.notFound().build()));
}

/**
* 根据ID 合并相同路由ID的路由过滤器
* @return
*/
@GetMapping("/routes/{id}/combinedfilters")
public Mono<HashMap<String, Object>> combinedfilters(@PathVariable String id) {
// TODO: missing global filters
return this.routeLocator.getRoutes().filter(route -> route.getId().equals(id))
.reduce(new HashMap<>(), this::putItem);
}

}

GatewayControllerEndpoint实现类如下:

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
/*
* 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.actuate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
* 网关路由端点实现类
* @RestControllerEndpoint 是Spring boot 2.x新增加的端点注解 仅支持http
* 和@Endpoint@WebEndpoint作用是一样的,都是为服务增加actuator 接口,方便管理运行中的服务
* @author Spencer Gibb
*/
@RestControllerEndpoint(id = "gateway")
public class GatewayControllerEndpoint extends AbstractGatewayControllerEndpoint {

public GatewayControllerEndpoint(List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
super(null, globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
routeLocator);
}

// TODO: Flush out routes without a definition

/**
* 获取路由
* @return
*/
@GetMapping("/routes")
public Flux<Map<String, Object>> routes() {
return this.routeLocator.getRoutes().map(this::serialize);
}

Map<String, Object> serialize(Route route) {
HashMap<String, Object> r = new HashMap<>();
r.put("route_id", route.getId());
r.put("uri", route.getUri().toString());
r.put("order", route.getOrder());
r.put("predicate", route.getPredicate().toString());
if (!CollectionUtils.isEmpty(route.getMetadata())) {
r.put("metadata", route.getMetadata());
}

ArrayList<String> filters = new ArrayList<>();

for (int i = 0; i < route.getFilters().size(); i++) {
GatewayFilter gatewayFilter = route.getFilters().get(i);
filters.add(gatewayFilter.toString());
}

r.put("filters", filters);
return r;
}

/**
* 根据ID获取单个路由
* @param id
* @return
*/
@GetMapping("/routes/{id}")
public Mono<ResponseEntity<Map<String, Object>>> route(@PathVariable String id) {
// @formatter:off
return this.routeLocator.getRoutes()
.filter(route -> route.getId().equals(id))
.singleOrEmpty()
.map(this::serialize)
.map(ResponseEntity::ok)
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
// @formatter:on
}

}

GatewayLegacyControllerEndpoint实现类如下:

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
/*
* 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.actuate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import reactor.core.publisher.Mono;

import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
* 网关路由端点实现类 -- 旧实现类 默认不会使用该实现
* @author Spencer Gibb
*/
@RestControllerEndpoint(id = "gateway")
public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEndpoint {

public GatewayLegacyControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> GatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
super(routeDefinitionLocator, globalFilters, GatewayFilters, routePredicates,
routeDefinitionWriter, routeLocator);
}

/**
* 获取路由
* @return
*/
@GetMapping("/routes")
public Mono<List<Map<String, Object>>> routes() {
Mono<Map<String, RouteDefinition>> routeDefs = this.routeDefinitionLocator
.getRouteDefinitions().collectMap(RouteDefinition::getId);
Mono<List<Route>> routes = this.routeLocator.getRoutes().collectList();
return Mono.zip(routeDefs, routes).map(tuple -> {
Map<String, RouteDefinition> defs = tuple.getT1();
List<Route> routeList = tuple.getT2();
List<Map<String, Object>> allRoutes = new ArrayList<>();

routeList.forEach(route -> {
HashMap<String, Object> r = new HashMap<>();
r.put("route_id", route.getId());
r.put("order", route.getOrder());

if (defs.containsKey(route.getId())) {
r.put("route_definition", defs.get(route.getId()));
}
else {
HashMap<String, Object> obj = new HashMap<>();

obj.put("predicate", route.getPredicate().toString());

if (!route.getFilters().isEmpty()) {
ArrayList<String> filters = new ArrayList<>();
for (GatewayFilter filter : route.getFilters()) {
filters.add(filter.toString());
}

obj.put("filters", filters);
}

if (!CollectionUtils.isEmpty(route.getMetadata())) {
obj.put("metadata", route.getMetadata());
}

if (!obj.isEmpty()) {
r.put("route_object", obj);
}
}
allRoutes.add(r);
});

return allRoutes;
});
}

/**
* 根据ID获取单个路由
* @return
*/
@GetMapping("/routes/{id}")
public Mono<ResponseEntity<RouteDefinition>> route(@PathVariable String id) {
// TODO: missing RouteLocator
return this.routeDefinitionLocator.getRouteDefinitions()
.filter(route -> route.getId().equals(id)).singleOrEmpty()
.map(ResponseEntity::ok)
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}

}

三 监控端点总结

下表列出了Spring Cloud Gateway执行器端点(请注意,每个端点都将 /actuator/gateway 作为基本路径)

IDHTTP Method描述
globalfiltersGET显示全局过滤器列表
routefiltersGET显示用于特定路由的GatewayFilter工厂列表
refreshPOST清除路由缓存
routesGET显示网关中定义的路由列表
routes/{id}GET显示有关特定路线的信息
routes/{id}POST新增一个路由到网关
routes/{id}DELETE从网关中删除现有路由