序
本文主要研究一下springcloud的GatewayControllerEndpoint
GatewayAutoConfiguration
spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
//......
@Bean
public AfterRoutePredicateFactory afterRoutePredicateFactory() {
return new AfterRoutePredicateFactory();
}
@Bean
public BeforeRoutePredicateFactory beforeRoutePredicateFactory() {
return new BeforeRoutePredicateFactory();
}
@Bean
public BetweenRoutePredicateFactory betweenRoutePredicateFactory() {
return new BetweenRoutePredicateFactory();
}
@Bean
public CookieRoutePredicateFactory cookieRoutePredicateFactory() {
return new CookieRoutePredicateFactory();
}
@Bean
public HeaderRoutePredicateFactory headerRoutePredicateFactory() {
return new HeaderRoutePredicateFactory();
}
@Bean
public HostRoutePredicateFactory hostRoutePredicateFactory() {
return new HostRoutePredicateFactory();
}
@Bean
public MethodRoutePredicateFactory methodRoutePredicateFactory() {
return new MethodRoutePredicateFactory();
}
@Bean
public PathRoutePredicateFactory pathRoutePredicateFactory() {
return new PathRoutePredicateFactory();
}
@Bean
public QueryRoutePredicateFactory queryRoutePredicateFactory() {
return new QueryRoutePredicateFactory();
}
@Bean
public ReadBodyPredicateFactory readBodyPredicateFactory(ServerCodecConfigurer codecConfigurer) {
return new ReadBodyPredicateFactory(codecConfigurer);
}
//......
@Configuration
@ConditionalOnClass(Health.class)
protected static class GatewayActuatorConfiguration {
@Bean
@ConditionalOnEnabledEndpoint
public GatewayControllerEndpoint gatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> GatewayFilters, RouteDefinitionWriter routeDefinitionWriter,
RouteLocator routeLocator) {
return new GatewayControllerEndpoint(routeDefinitionLocator, globalFilters, GatewayFilters, routeDefinitionWriter, routeLocator);
}
}
}
可以看到最后有一个GatewayActuatorConfiguration,在有actuator类库的前提下则会配置,配置的是GatewayControllerEndpoint
GatewayControllerEndpoint
spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/actuate/GatewayControllerEndpoint.java
@RestControllerEndpoint(id = "gateway")
public class GatewayControllerEndpoint implements ApplicationEventPublisherAware {
private static final Log log = LogFactory.getLog(GatewayControllerEndpoint.class);
private RouteDefinitionLocator routeDefinitionLocator;
private List<GlobalFilter> globalFilters;
private List<GatewayFilterFactory> GatewayFilters;
private RouteDefinitionWriter routeDefinitionWriter;
private RouteLocator routeLocator;
private ApplicationEventPublisher publisher;
public GatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> GatewayFilters, RouteDefinitionWriter routeDefinitionWriter,
RouteLocator routeLocator) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.globalFilters = globalFilters;
this.GatewayFilters = GatewayFilters;
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
@PostMapping("/refresh")
public Mono<Void> refresh() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return Mono.empty();
}
@GetMapping("/globalfilters")
public Mono<HashMap<String, Object>> globalfilters() {
return getNamesToOrders(this.globalFilters);
}
@GetMapping("/routefilters")
public Mono<HashMap<String, Object>> routefilers() {
return getNamesToOrders(this.GatewayFilters);
}
private <T> Mono<HashMap<String, Object>> getNamesToOrders(List<T> list) {
return Flux.fromIterable(list).reduce(new HashMap<>(), this::putItem);
}
private HashMap<String, Object> putItem(HashMap<String, Object> map, Object o) {
Integer order = null;
if (o instanceof Ordered) {
order = ((Ordered)o).getOrder();
}
//filters.put(o.getClass().getName(), order);
map.put(o.toString(), order);
return map;
}
// TODO: Flush out routes without a definition
@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 (!obj.isEmpty()) {
r.put("route_object", obj);
}
}
allRoutes.add(r);
});
return allRoutes;
});
}
/*
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<Void>> save(@PathVariable String id, @RequestBody Mono<RouteDefinition> route) {
return this.routeDefinitionWriter.save(route.map(r -> {
r.setId(id);
log.debug("Saving route: " + route);
return r;
})).then(Mono.defer(() ->
Mono.just(ResponseEntity.created(URI.create("/routes/"+id)).build())
));
}
@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()));
}
@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(route -> ResponseEntity.ok(route))
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}
@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);
}
}
可以看到提供了如下几个rest api
- POST /refresh
- GET /globalfilters
- GET /routefilters
- GET /routes
- POST /routes/{id}
- GET /routes/{id}
- GET /routes/{id}/combinedfilters
实例
/actuator/gateway/routes
[
{
"route_id": "CompositeDiscoveryClient_DISCOVERY-SERVICE",
"route_definition": {
"id": "CompositeDiscoveryClient_DISCOVERY-SERVICE",
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/DISCOVERY-SERVICE/**"
}
}
],
"filters": [
{
"name": "RewritePath",
"args": {
"regexp": "/DISCOVERY-SERVICE/(?<remaining>.*)",
"replacement": "/${remaining}"
}
}
],
"uri": "lb://DISCOVERY-SERVICE",
"order": 0
},
"order": 0
},
{
"route_id": "CompositeDiscoveryClient_GATEWAY-SERVICE",
"route_definition": {
"id": "CompositeDiscoveryClient_GATEWAY-SERVICE",
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/GATEWAY-SERVICE/**"
}
}
],
"filters": [
{
"name": "RewritePath",
"args": {
"regexp": "/GATEWAY-SERVICE/(?<remaining>.*)",
"replacement": "/${remaining}"
}
}
],
"uri": "lb://GATEWAY-SERVICE",
"order": 0
},
"order": 0
}
]
/actuator/gateway/globalfilters
{
"org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@f425231": 10100,
"org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@5cbd94b2": -1,
"org.springframework.cloud.gateway.filter.NettyRoutingFilter@506aabf6": 2147483647,
"org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@756aadfc": 10000,
"org.springframework.cloud.gateway.filter.ForwardRoutingFilter@705a8dbc": 2147483647,
"org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@6824b913": 2147483637,
"org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@40729f01": 2147483646
}
/actuator/gateway/routefilters
{
"[RedirectToGatewayFilterFactory@32f96bba configClass = RedirectToGatewayFilterFactory.Config]": null,
"[StripPrefixGatewayFilterFactory@4a481728 configClass = StripPrefixGatewayFilterFactory.Config]": null,
"[RemoveResponseHeaderGatewayFilterFactory@67e25252 configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[RequestHeaderToRequestUriGatewayFilterFactory@4ace284d configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[ModifyRequestBodyGatewayFilterFactory@b3e86d5 configClass = ModifyRequestBodyGatewayFilterFactory.Config]": null,
"[RemoveRequestHeaderGatewayFilterFactory@611640f0 configClass = AbstractGatewayFilterFactory.NameConfig]": null,
"[SetStatusGatewayFilterFactory@3fd05b3e configClass = SetStatusGatewayFilterFactory.Config]": null,
"[PreserveHostHeaderGatewayFilterFactory@4d0e54e0 configClass = Object]": null,
"[SetResponseHeaderGatewayFilterFactory@1682c08c configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[SecureHeadersGatewayFilterFactory@76ececd configClass = Object]": null,
"[SaveSessionGatewayFilterFactory@4eb9f2af configClass = Object]": null,
"[AddResponseHeaderGatewayFilterFactory@75b6dd5b configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[PrefixPathGatewayFilterFactory@e111c7c configClass = PrefixPathGatewayFilterFactory.Config]": null,
"[SetRequestHeaderGatewayFilterFactory@7affc159 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[RewritePathGatewayFilterFactory@58f4b31a configClass = RewritePathGatewayFilterFactory.Config]": null,
"[SetPathGatewayFilterFactory@72eb6200 configClass = SetPathGatewayFilterFactory.Config]": null,
"[RetryGatewayFilterFactory@21a9a705 configClass = RetryGatewayFilterFactory.Retry]": null,
"[AddRequestHeaderGatewayFilterFactory@1f1cddf3 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[ModifyResponseBodyGatewayFilterFactory@72b43104 configClass = ModifyResponseBodyGatewayFilterFactory.Config]": null,
"[AddRequestParameterGatewayFilterFactory@228bda54 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null
}
/actuator/gateway/routes/CompositeDiscoveryClient_DISCOVERY-SERVICE
{
"id": "CompositeDiscoveryClient_DISCOVERY-SERVICE",
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/DISCOVERY-SERVICE/**"
}
}
],
"filters": [
{
"name": "RewritePath",
"args": {
"regexp": "/DISCOVERY-SERVICE/(?<remaining>.*)",
"replacement": "/${remaining}"
}
}
],
"uri": "lb://DISCOVERY-SERVICE",
"order": 0
}
/actuator/gateway/routes/CompositeDiscoveryClient_DISCOVERY-SERVICE/combinedfilters
{
"Route{id='CompositeDiscoveryClient_DISCOVERY-SERVICE', uri=lb://DISCOVERY-SERVICE, order=0, predicate=org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$769/1774209584@7d9639ad, gatewayFilters=[OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory$$Lambda$771/1052389952@528c8627, order=1}]}": 0
}
小结
springcloud gateway提供了一个gateway actuator,该endpiont提供了关于filter及routes的信息查询以及指定route信息更新的rest api,这给web界面提供管理配置功能提供了极大的便利。