源码分析-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才会使用这个实现,而不使用新的实现三个类的关系如图:
二 源码阅读之代码阅读 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 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;public class AbstractGatewayControllerEndpoint implements ApplicationEventPublisherAware { private static final Log log = LogFactory.getLog(GatewayControllerEndpoint.class); protected RouteDefinitionLocator routeDefinitionLocator; protected List<GlobalFilter> globalFilters; 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; } @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); } @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); } private HashMap<String, Object> putItem (HashMap<String, Object> map, Object o) { Integer order = null ; if (o instanceof Ordered) { order = ((Ordered) o).getOrder(); } map.put(o.toString(), order); return map; } @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; } @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}/combinedfilters" ) public Mono<HashMap<String, Object>> combinedfilters(@PathVariable String id) { 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 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 (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); } @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; } @GetMapping ("/routes/{id}" ) public Mono<ResponseEntity<Map<String, Object>>> route(@PathVariable String id) { return this .routeLocator.getRoutes() .filter(route -> route.getId().equals(id)) .singleOrEmpty() .map(this ::serialize) .map(ResponseEntity::ok) .switchIfEmpty(Mono.just(ResponseEntity.notFound().build())); } }
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 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;@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); } @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; }); } @GetMapping ("/routes/{id}" ) public Mono<ResponseEntity<RouteDefinition>> route(@PathVariable String id) { 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
作为基本路径)
ID HTTP Method 描述 globalfilters
GET 显示全局过滤器列表 routefilters
GET 显示用于特定路由的GatewayFilter工厂列表 refresh
POST 清除路由缓存 routes
GET 显示网关中定义的路由列表 routes/{id}
GET 显示有关特定路线的信息 routes/{id}
POST 新增一个路由到网关 routes/{id}
DELETE 从网关中删除现有路由