SpringCloudGateway动态路由(Redis持久化)以及请求到寻找路由的流程
1、路由加载流程简述
/**
* 1、处理映射器加载路由 {@link RoutePredicateHandlerMapping#lookupRoute(ServerWebExchange)}
* 2、路由加载器获取路由 {@link RouteLocator#getRoutes()} ;{@link RouteLocator}注入到Spring容器的是统一入口{@link CachingRouteLocator},
* {@link CompositeRouteLocator#getRoutes()} 在 {@link GatewayAutoConfiguration#cachedCompositeRouteLocator(List)} 注入到Spring容器中,
* 3、路由定义信息加载器 方法cachedCompositeRouteLocator 中 List<RouteLocator> routeLocators 中,
* 其中之一{@link RouteDefinitionRouteLocator}
* 在{@link GatewayAutoConfiguration#routeDefinitionRouteLocator(GatewayProperties, List, List,RouteDefinitionLocator, ConfigurationService)} 注入到Spring容器中,
* 其中 {@link RouteDefinitionLocator}(路由定义信息加载器)是统一入口 {@link CompositeRouteDefinitionLocator} 在{@link GatewayAutoConfiguration#routeDefinitionLocator(List)}注入到Spring容器中,
* 其中 List<RouteDefinitionLocator> routeDefinitionLocators 包含
* 读取配置文件的 {@link GatewayAutoConfiguration#propertiesRouteDefinitionLocator(GatewayProperties)}
* 不存在持久化bean的时候 {@link GatewayAutoConfiguration#inMemoryRouteDefinitionRepository()}
* 4、加载路由定义信息 {@link RouteDefinitionLocator#getRouteDefinitions()} {@link CompositeRouteDefinitionLocator#getRouteDefinitions()} 会统一加载,所有的 RouteDefinitionLocator bean
*
* 流程如下 {@link RoutePredicateHandlerMapping#lookupRoute(ServerWebExchange)}
* {@link RouteLocator#getRoutes()} --[{@link CompositeRouteLocator#getRoutes()} {@link CachingRouteLocator#getRoutes()} 主要
* {@link RouteDefinitionRouteLocator#getRoutes()},初始化过滤器、断言等都在此类 ]
* {@link RouteDefinitionRouteLocator} 中的{@link RouteDefinitionLocator#getRouteDefinitions()}获取路由信息,{@link RouteDefinitionRouteLocator#convertToRoute(RouteDefinition)}
* 路由信息转换为路由
*
* RoutePredicateHandlerMapping 对应的映射,访问一次都会加载一次
* @return
*/
2、动态路由,redis存储
- Controller
@RestController
public class GatewayRouteController extends GatewayBaseController{
@Autowired
DynamicRouteService routeService;
@Autowired
GatewayAppRouteRedisRepository redisRepository;
// @PostMapping(value = "add", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
// public Response save(@RequestBody GatewayRouteDefinition definition) {
// routeService.save(definition);
// return Response.success();
// }
@PostMapping("route/save_or_update")
public Mono<RestResult> update(@RequestBody GatewayAppRoute definition) {
routeService.update(definition);
redisRepository.update(definition);
return Mono.just(TRestBuilder.success());
}
@DeleteMapping("route/{id}")
public Mono<RestResult> delete(@PathVariable("id") String id) {
routeService.delete(id);
redisRepository.delete(id);
return Mono.just(TRestBuilder.success());
}
@PostMapping("routes")
public Mono<RestResult<List<GatewayAppRoute>>> list() {
return Mono.just(TRestBuilder.success(redisRepository.routeViews()));
}
}
- 动态路由
/**
* 动态路由信息
*/
public class DynamicRouteService implements ApplicationEventPublisherAware {
@Autowired
RouteDefinitionRepository routeDefinitionRepository;// 实现redis存储
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher=applicationEventPublisher;
}
// 发布事件,刷新路由,实现动态路由
private void publish() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
public void save(GatewayAppRoute definition){
RouteDefinition routeDefinition=definition.routeDefinition();
routeDefinitionRepository.save(Mono.just(routeDefinition)).subscribe();
publish();
}
public void update(GatewayAppRoute definition){
RouteDefinition routeDefinition=definition.routeDefinition();
routeDefinitionRepository.delete(Mono.just(definition.getServiceId()));
routeDefinitionRepository.save(Mono.just(routeDefinition)).subscribe();
publish();
}
public void delete(String serviceId){
routeDefinitionRepository.delete(Mono.just(serviceId));
publish();
}
}
- Redis持久化
/**
* 路由存储 redis
*/
public class RouteDefinitionRedisRepository implements RouteDefinitionRepository,RouteRepository {
final static String GATEWAY_ROUTES = "GATEWAY:ROUTE";
@Autowired
RedisCache redisCache;//redis操作,Redistemplate
/**
* 从redis加在路由信息
* @return
*/
public Map<String,RouteDefinition> loadRoute() {
Map<String, RouteDefinition> data = redisCache.getCacheMap(GATEWAY_ROUTES, RouteDefinition.class);
return data;
}
/**
* 1、处理映射器加载路由 {@link RoutePredicateHandlerMapping#lookupRoute(ServerWebExchange)}
* 2、路由加载器获取路由 {@link RouteLocator#getRoutes()} ;{@link RouteLocator}注入到Spring容器的是统一入口{@link CachingRouteLocator},
* {@link CompositeRouteLocator#getRoutes()} 在 {@link GatewayAutoConfiguration#cachedCompositeRouteLocator(List)} 注入到Spring容器中,
* 3、路由定义信息加载器 方法cachedCompositeRouteLocator 中 List<RouteLocator> routeLocators 中,
* 其中之一{@link RouteDefinitionRouteLocator}
* 在{@link GatewayAutoConfiguration#routeDefinitionRouteLocator(GatewayProperties, List, List,RouteDefinitionLocator, ConfigurationService)} 注入到Spring容器中,
* 其中 {@link RouteDefinitionLocator}(路由定义信息加载器)是统一入口 {@link CompositeRouteDefinitionLocator} 在{@link GatewayAutoConfiguration#routeDefinitionLocator(List)}注入到Spring容器中,
* 其中 List<RouteDefinitionLocator> routeDefinitionLocators 包含
* 读取配置文件的 {@link GatewayAutoConfiguration#propertiesRouteDefinitionLocator(GatewayProperties)}
* 不存在持久化bean的时候 {@link GatewayAutoConfiguration#inMemoryRouteDefinitionRepository()}
* 4、加载路由定义信息 {@link RouteDefinitionLocator#getRouteDefinitions()} {@link CompositeRouteDefinitionLocator#getRouteDefinitions()} 会统一加载,所有的 RouteDefinitionLocator bean
*
* 流程如下 {@link RoutePredicateHandlerMapping#lookupRoute(ServerWebExchange)}
* {@link RouteLocator#getRoutes()} --[{@link CompositeRouteLocator#getRoutes()} {@link CachingRouteLocator#getRoutes()} 主要
* {@link RouteDefinitionRouteLocator#getRoutes()},初始化过滤器、断言等都在此类 ]
* {@link RouteDefinitionRouteLocator} 中的{@link RouteDefinitionLocator#getRouteDefinitions()}获取路由信息,{@link RouteDefinitionRouteLocator#convertToRoute(RouteDefinition)}
* 路由信息转换为路由
*
* RoutePredicateHandlerMapping 对应的映射,访问一次都会加载一次
* @return
*/
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<RouteDefinition> routeDefinitions = new ArrayList<>();
Map<String, RouteDefinition> data = loadRoute();
if (!CollectionUtils.isEmpty(data)) {
data.forEach((K, V) -> routeDefinitions.add(V));
}
return Flux.fromIterable(routeDefinitions);
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(routeDefinition -> {
redisCache.putDataToCacheMap(GATEWAY_ROUTES, routeDefinition.getId(), routeDefinition);
return Mono.empty();
});
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id -> {
if (redisCache.cacheMapHasKey(GATEWAY_ROUTES, id)) {
redisCache.deleteDataFromCacheMap(GATEWAY_ROUTES, id);
return Mono.empty();
}
return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId)));
});
}
@Override
public List<RouteDefinition> loadRoutes() {
Map<String,RouteDefinition> data=loadRoute();
return CollectionUtils.isEmpty(data)?new ArrayList<>():new ArrayList<>(data.values());
}
}
- GatewayAppRoute
/**
* APP 配置信息
*/
@Data
public class GatewayAppRoute {
/**
* 服务状态
*/
private int serviceStatus;
/**
* 服务名称
*/
@NotBlank(message = "serviceName不能为空")
private String serviceName;
/**
* 服务版本号
*/
@NotBlank(message = "serviceVersion不能为空")
private String serviceVersion;
/**
* 路由ID(服务ID)
*/
@NotBlank(message = "serviceId不能为空")
private String serviceId;
/**
* 路由断言集合配置
*/
@NotEmpty(message = "predicates不能为空")
private List<GatewayDefinition> predicates = new ArrayList<>();
/**
* 路由过滤器集合配置
*/
private List<GatewayDefinition> filters = new ArrayList<>();
/**
* 路由规则转发的目标uri
*
* 建议lb协议<lb://serviceId>
*/
@NotBlank(message = "uri不能为空")
private String uri;
/**
* 路由执行顺序
*/
private int order = 0;
@Data
public static class GatewayDefinition {
//断言(过滤器)对应的Name
private String name;
//配置的断言(过滤器)规则
private Map<String,String> args=new LinkedHashMap<>();
}
public RouteDefinition routeDefinition(){
RouteDefinition routeDefinition=new RouteDefinition();
routeDefinition.setId(serviceId);
routeDefinition.setOrder(order);
URI routeUri = !StringUtils.startsWith(uri,"lb")?UriComponentsBuilder.fromHttpUrl(uri).build().toUri():URI.create(uri);
routeDefinition.setUri(routeUri);
if(!CollectionUtils.isEmpty(filters)){
routeDefinition.setFilters(filters.stream().map(f->{
FilterDefinition filterDefinition=new FilterDefinition();
filterDefinition.setName(f.name);
filterDefinition.setArgs(f.args);
return filterDefinition;
}).collect(Collectors.toList()));
}
if(!CollectionUtils.isEmpty(predicates)){
routeDefinition.setPredicates(predicates.stream().map(f->{
PredicateDefinition predicateDefinition=new PredicateDefinition();
predicateDefinition.setName(f.name);
predicateDefinition.setArgs(f.args);
return predicateDefinition;
}).collect(Collectors.toList()));
}
return routeDefinition;
}
}
3. SpringCloudGateway 请求到寻找路由的流程
DispatcherHandler
package org.springframework.web.reactive;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
/**
* Central dispatcher for HTTP request handlers/controllers. Dispatches to
* registered handlers for processing a request, providing convenient mapping
* facilities.
*
* <p>{@code DispatcherHandler} discovers the delegate components it needs from
* Spring configuration. It detects the following in the application context:
* <ul>
* <li>{@link HandlerMapping} -- map requests to handler objects
* <li>{@link HandlerAdapter} -- for using any handler interface
* <li>{@link HandlerResultHandler} -- process handler return values
* </ul>
*
* <p>{@code DispatcherHandler} is also designed to be a Spring bean itself and
* implements {@link ApplicationContextAware} for access to the context it runs
* in. If {@code DispatcherHandler} is declared with the bean name "webHandler"
* it is discovered by {@link WebHttpHandlerBuilder#applicationContext} which
* creates a processing chain together with {@code WebFilter},
* {@code WebExceptionHandler} and others.
*
* <p>A {@code DispatcherHandler} bean declaration is included in
* {@link org.springframework.web.reactive.config.EnableWebFlux @EnableWebFlux}
* configuration.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @author Juergen Hoeller
* @since 5.0
* @see WebHttpHandlerBuilder#applicationContext(ApplicationContext)
*/
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
@Nullable
private List<HandlerMapping> handlerMappings;
@Nullable
private List<HandlerAdapter> handlerAdapters;
@Nullable
private List<HandlerResultHandler> resultHandlers;
/**
* Create a new {@code DispatcherHandler} which needs to be configured with
* an {@link ApplicationContext} through {@link #setApplicationContext}.
*/
public DispatcherHandler() {
}
/**
* Create a new {@code DispatcherHandler} for the given {@link ApplicationContext}.
* @param applicationContext the application context to find the handler beans in
*/
public DispatcherHandler(ApplicationContext applicationContext) {
initStrategies(applicationContext);
}
/**
* Return all {@link HandlerMapping} beans detected by type in the
* {@link #setApplicationContext injected context} and also
* {@link AnnotationAwareOrderComparator#sort(List) sorted}.
* <p><strong>Note:</strong> This method may return {@code null} if invoked
* prior to {@link #setApplicationContext(ApplicationContext)}.
* @return immutable list with the configured mappings or {@code null}
*/
@Nullable
public final List<HandlerMapping> getHandlerMappings() {
return this.handlerMappings;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
initStrategies(applicationContext);
}
// 初始化处理
protected void initStrategies(ApplicationContext context) {
Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class, true, false);
ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
AnnotationAwareOrderComparator.sort(mappings);
this.handlerMappings = Collections.unmodifiableList(mappings);
Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerAdapter.class, true, false);
this.handlerAdapters = new ArrayList<>(adapterBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerResultHandler.class, true, false);
this.resultHandlers = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(this.resultHandlers);
}
// 获取映射处理器
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
return Flux.fromIterable(this.handlerMappings)
// 获取处理器,与已经注册好了的HandlerAdapter一一匹配
// org.springframework.web.reactive.handler.AbstractHandlerMapping#getHandler
//AbstractHandlerMapping 有多个子类,子类都会进入父类getHandler
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
.switchIfEmpty(createNotFoundError())
// 执行
.flatMap(handler -> invokeHandler(exchange, handler))
// 结果处理
.flatMap(result -> handleResult(exchange, result));
}
private <R> Mono<R> createNotFoundError() {
return Mono.defer(() -> {
Exception ex = new ResponseStatusException(HttpStatus.NOT_FOUND, "No matching handler");
return Mono.error(ex);
});
}
// 看哪一种HandlerAdapter是支持该controller类型的
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
if (this.handlerAdapters != null) {
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
if (handlerAdapter.supports(handler)) {
return handlerAdapter.handle(exchange, handler);
}
}
}
return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}
private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
return getResultHandler(result).handleResult(exchange, result)
.checkpoint("Handler " + result.getHandler() + " [DispatcherHandler]")
.onErrorResume(ex ->
result.applyExceptionHandler(ex).flatMap(exResult -> {
String text = "Exception handler " + exResult.getHandler() +
", error=\"" + ex.getMessage() + "\" [DispatcherHandler]";
return getResultHandler(exResult).handleResult(exchange, exResult).checkpoint(text);
}));
}
private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
if (this.resultHandlers != null) {
for (HandlerResultHandler resultHandler : this.resultHandlers) {
if (resultHandler.supports(handlerResult)) {
return resultHandler;
}
}
}
throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
}
}
其中 org.springframework.web.reactive.DispatcherHandler#handle(ServerWebExchange exchange)
根据请求从handlerMappings
获取对应的处理器映射,通过处理器映射,可以将web请求映射到正确的处理器(handler)上。
@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {
return getHandlerInternal(exchange).map(handler -> {
if (logger.isDebugEnabled()) {
logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
}
ServerHttpRequest request = exchange.getRequest();
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
return REQUEST_HANDLED_HANDLER;
}
}
return handler;
});
}
/**
* Look up a handler for the given request, returning an empty {@code Mono}
* if no specific one is found. This method is called by {@link #getHandler}.
* <p>On CORS pre-flight requests this method should return a match not for
* the pre-flight request but for the expected actual request based on the URL
* path, the HTTP methods from the "Access-Control-Request-Method" header, and
* the headers from the "Access-Control-Request-Headers" header.
* @param exchange current exchange
* @return {@code Mono} for the matching handler, if any
*/
protected abstract Mono<?> getHandlerInternal(ServerWebExchange exchange);
这里,看到了gateway的RoutePredicateHandlerMapping
org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping
RoutePredicateHandlerMapping
在 org.springframework.cloud.gateway.config.GatewayAutoConfiguration
初始化
package org.springframework.cloud.gateway.handler;
import java.util.function.Function;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.config.GlobalCorsProperties;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.core.env.Environment;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping.ManagementPortType.DIFFERENT;
import static org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping.ManagementPortType.DISABLED;
import static org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping.ManagementPortType.SAME;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
/**
* @author Spencer Gibb
*/
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private final FilteringWebHandler webHandler;
// 路由统一加载的实例(org.springframework.cloud.gateway.route.CompositeRouteLocator)
// org.springframework.cloud.gateway.route.CachingRouteLocator
private final RouteLocator routeLocator;
private final Integer managementPort;
private final ManagementPortType managementPortType;
public RoutePredicateHandlerMapping(FilteringWebHandler webHandler,
RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties,
Environment environment) {
this.webHandler = webHandler;
this.routeLocator = routeLocator;
this.managementPort = getPortProperty(environment, "management.server.");
this.managementPortType = getManagementPortType(environment);
setOrder(1);
setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
}
private ManagementPortType getManagementPortType(Environment environment) {
Integer serverPort = getPortProperty(environment, "server.");
if (this.managementPort != null && this.managementPort < 0) {
return DISABLED;
}
return ((this.managementPort == null
|| (serverPort == null && this.managementPort.equals(8080))
|| (this.managementPort != 0 && this.managementPort.equals(serverPort)))
? SAME : DIFFERENT);
}
private static Integer getPortProperty(Environment environment, String prefix) {
return environment.getProperty(prefix + "port", Integer.class);
}
// 网关路由时进入此方法
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
// don't handle requests on management port if set and different than server port
if (this.managementPortType == DIFFERENT && this.managementPort != null
&& exchange.getRequest().getURI().getPort() == this.managementPort) {
return Mono.empty();
}
exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());
// 获取配置路由
return lookupRoute(exchange)
// .log("route-predicate-handler-mapping", Level.FINER) //name this
.flatMap((Function<Route, Mono<?>>) r -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isDebugEnabled()) {
logger.debug(
"Mapping [" + getExchangeDesc(exchange) + "] to " + r);
}
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
return Mono.just(webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isTraceEnabled()) {
logger.trace("No RouteDefinition found for ["
+ getExchangeDesc(exchange) + "]");
}
})));
}
@Override
protected CorsConfiguration getCorsConfiguration(Object handler,
ServerWebExchange exchange) {
// TODO: support cors configuration via properties on a route see gh-229
// see RequestMappingHandlerMapping.initCorsConfiguration()
// also see
// https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsWebFilterTests.java
return super.getCorsConfiguration(handler, exchange);
}
// TODO: get desc from factory?
private String getExchangeDesc(ServerWebExchange exchange) {
StringBuilder out = new StringBuilder();
out.append("Exchange: ");
out.append(exchange.getRequest().getMethod());
out.append(" ");
out.append(exchange.getRequest().getURI());
return out.toString();
}
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
// 获取加载路由
return this.routeLocator.getRoutes()
// individually filter routes so that filterWhen error delaying is not a
// problem
.concatMap(route -> Mono.just(route).filterWhen(r -> {
// add the current route we are testing
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return r.getPredicate().apply(exchange);
})
// instead of immediately stopping main flux due to error, log and
// swallow it
.doOnError(e -> logger.error(
"Error applying predicate for route: " + route.getId(),
e))
.onErrorResume(e -> Mono.empty()))
// .defaultIfEmpty() put a static Route not found
// or .switchIfEmpty()
// .switchIfEmpty(Mono.<Route>empty().log("noroute"))
.next()
// TODO: error handling
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("Route matched: " + route.getId());
}
validateRoute(route, exchange);
return route;
});
/*
* TODO: trace logging if (logger.isTraceEnabled()) {
* logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
*/
}
/**
* Validate the given handler against the current request.
* <p>
* The default implementation is empty. Can be overridden in subclasses, for example
* to enforce specific preconditions expressed in URL mappings.
* @param route the Route object to validate
* @param exchange current exchange
* @throws Exception if validation failed
*/
@SuppressWarnings("UnusedParameters")
protected void validateRoute(Route route, ServerWebExchange exchange) {
}
protected String getSimpleName() {
return "RoutePredicateHandlerMapping";
}
public enum ManagementPortType {
/**
* The management port has been disabled.
*/
DISABLED,
/**
* The management port is the same as the server port.
*/
SAME,
/**
* The management port and server port are different.
*/
DIFFERENT;
}
}
RouteLocator路由加载
package org.springframework.cloud.gateway.route;
import reactor.core.publisher.Flux;
/**
* org.springframework.cloud.gateway.route.CachingRouteLocator 缓存,有事件监听
* org.springframework.cloud.gateway.route.CompositeRouteLocator 组合多种实现,提供统一入口
* org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator 将路由定义信息转化为路由
* @author Spencer Gibb
*/
// TODO: rename to Routes?
public interface RouteLocator {
Flux<Route> getRoutes();
}
GatewayAutoConfiguration 加载代码
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator,
ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
gatewayFilters, properties, configurationService);
}
@Bean
@Primary
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
// TODO: property to disable composite?
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(
new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
- RouteDefinitionRouteLocator
package org.springframework.cloud.gateway.route;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Flux;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.event.FilterArgsEvent;
import org.springframework.cloud.gateway.event.PredicateArgsEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.handler.AsyncPredicate;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.support.ConfigurationService;
import org.springframework.cloud.gateway.support.HasRouteId;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.convert.ConversionService;
import org.springframework.validation.Validator;
import org.springframework.web.server.ServerWebExchange;
/**
* {@link RouteLocator} that loads routes from a {@link RouteDefinitionLocator}.
*
* @author Spencer Gibb
*/
public class RouteDefinitionRouteLocator
implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
/**
* Default filters name.
*/
public static final String DEFAULT_FILTERS = "defaultFilters";
protected final Log logger = LogFactory.getLog(getClass());
// 路由定义信息
private final RouteDefinitionLocator routeDefinitionLocator;
private final ConfigurationService configurationService;
// 路由RoutePredicate工厂,配置该路由决定采取哪种路由规则,如何配置前缀就可以找到对应的实例??
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
// 路由GatewayFilter工厂,配置该路由决定走哪些过滤器,如何配置前缀就可以找到对应的实例??
private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();
private final GatewayProperties gatewayProperties;
@Deprecated
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RoutePredicateFactory> predicates,
List<GatewayFilterFactory> gatewayFilterFactories,
GatewayProperties gatewayProperties, ConversionService conversionService) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.configurationService = new ConfigurationService();
this.configurationService.setConversionService(conversionService);
initFactories(predicates);
gatewayFilterFactories.forEach(
factory -> this.gatewayFilterFactories.put(factory.name(), factory));
this.gatewayProperties = gatewayProperties;
}
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RoutePredicateFactory> predicates,
List<GatewayFilterFactory> gatewayFilterFactories,
GatewayProperties gatewayProperties,
ConfigurationService configurationService) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.configurationService = configurationService;
// 配置前缀就可以找到对应的实例,将容器中的bean先放到内存
initFactories(predicates);
// 配置前缀就可以找到对应的实例,将容器中的bean先放到内存
gatewayFilterFactories.forEach(
factory -> this.gatewayFilterFactories.put(factory.name(), factory));
this.gatewayProperties = gatewayProperties;
}
@Override
@Deprecated
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (this.configurationService.getBeanFactory() == null) {
this.configurationService.setBeanFactory(beanFactory);
}
}
@Autowired
@Deprecated
public void setValidator(Validator validator) {
if (this.configurationService.getValidator() == null) {
this.configurationService.setValidator(validator);
}
}
@Override
@Deprecated
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
if (this.configurationService.getPublisher() == null) {
this.configurationService.setApplicationEventPublisher(publisher);
}
}
// 配置前缀就可以找到对应的实例,将容器中的bean先放到内存
private void initFactories(List<RoutePredicateFactory> predicates) {
predicates.forEach(factory -> {
String key = factory.name();
if (this.predicates.containsKey(key)) {
this.logger.warn("A RoutePredicateFactory named " + key
+ " already exists, class: " + this.predicates.get(key)
+ ". It will be overwritten.");
}
this.predicates.put(key, factory);
if (logger.isInfoEnabled()) {
logger.info("Loaded RoutePredicateFactory [" + key + "]");
}
});
}
@Override
public Flux<Route> getRoutes() {
// 将路由定义信息转化成路由信息
Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute);
if (!gatewayProperties.isFailOnRouteDefinitionError()) {
// instead of letting error bubble up, continue
routes = routes.onErrorContinue((error, obj) -> {
if (logger.isWarnEnabled()) {
logger.warn("RouteDefinition id " + ((RouteDefinition) obj).getId()
+ " will be ignored. Definition has invalid configs, "
+ error.getMessage());
}
});
}
return routes.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
// 转换成路由信息
private Route convertToRoute(RouteDefinition routeDefinition) {
AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
return Route.async(routeDefinition).asyncPredicate(predicate)
.replaceFilters(gatewayFilters).build();
}
@SuppressWarnings("unchecked")
// 加载路由网关过滤器
List<GatewayFilter> loadGatewayFilters(String id,
List<FilterDefinition> filterDefinitions) {
ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
for (int i = 0; i < filterDefinitions.size(); i++) {
FilterDefinition definition = filterDefinitions.get(i);
// 获取网关过滤器工厂
GatewayFilterFactory factory = this.gatewayFilterFactories
.get(definition.getName());
if (factory == null) {
throw new IllegalArgumentException(
"Unable to find GatewayFilterFactory with name "
+ definition.getName());
}
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + id + " applying filter "
+ definition.getArgs() + " to " + definition.getName());
}
// @formatter:off
// 构造每个工厂里对应的config信息
Object configuration = this.configurationService.with(factory)
.name(definition.getName())
.properties(definition.getArgs())
.eventFunction((bound, properties) -> new FilterArgsEvent(
// TODO: why explicit cast needed or java compile fails
RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties))
.bind();
// @formatter:on
// some filters require routeId
// TODO: is there a better place to apply this?
if (configuration instanceof HasRouteId) {
HasRouteId hasRouteId = (HasRouteId) configuration;
hasRouteId.setRouteId(id);
}
// 获取过滤器是实例
GatewayFilter gatewayFilter = factory.apply(configuration);
if (gatewayFilter instanceof Ordered) {
ordered.add(gatewayFilter);
}
else {
ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
}
}
return ordered;
}
// 获取过滤器,并根据ordered决定加载顺序
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
List<GatewayFilter> filters = new ArrayList<>();
// TODO: support option to apply defaults after route specific filters?
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
new ArrayList<>(this.gatewayProperties.getDefaultFilters())));
}
if (!routeDefinition.getFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(routeDefinition.getId(),
new ArrayList<>(routeDefinition.getFilters())));
}
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
private AsyncPredicate<ServerWebExchange> combinePredicates(
RouteDefinition routeDefinition) {
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
if (predicates == null || predicates.isEmpty()) {
// this is a very rare case, but possible, just match all
return AsyncPredicate.from(exchange -> true);
}
AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
predicates.get(0));
for (PredicateDefinition andPredicate : predicates.subList(1,
predicates.size())) {
AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
andPredicate);
predicate = predicate.and(found);
}
return predicate;
}
//加载断言
@SuppressWarnings("unchecked")
private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route,
PredicateDefinition predicate) {
// 获取断言工厂
RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
if (factory == null) {
throw new IllegalArgumentException(
"Unable to find RoutePredicateFactory with name "
+ predicate.getName());
}
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + route.getId() + " applying "
+ predicate.getArgs() + " to " + predicate.getName());
}
// @formatter:off
Object config = this.configurationService.with(factory)
.name(predicate.getName())
.properties(predicate.getArgs())
.eventFunction((bound, properties) -> new PredicateArgsEvent(
RouteDefinitionRouteLocator.this, route.getId(), properties))
.bind();
// @formatter:on
// 转换实例信息
return factory.applyAsync(config);
}
}
RouteDefinition路由定义
SpringCloudGateway通过RouteDefinition来转换生成具体的路由信息。RouteDefinition的信息是怎么加载初始化到网关系统中的,主要是通过RouteDefinitionLocator(路由定义信息加载器)接口,实现RouteDefinition初始化加载
- RouteDefinitionLocator源码
/**
* 路由定义信息的定位器,
* 负责读取路由配置( org.springframework.cloud.gateway.route.RouteDefinition
* 子类实现类
* 1.CachingRouteDefinitionLocator -RouteDefinitionLocator包装类, 缓存目标RouteDefinitionLocator 为routeDefinitions提供缓存功能
* 2.CompositeRouteDefinitionLocator -RouteDefinitionLocator包装类,组合多种 RouteDefinitionLocator 的实现,为 routeDefinitions提供统一入口
* 3.PropertiesRouteDefinitionLocator-从配置文件(GatewayProperties 例如,YML / Properties 等 ) 读取RouteDefinition
* 4.DiscoveryClientRouteDefinitionLocator-从注册中心( 例如,Eureka / Consul / Zookeeper / Etcd 等 )读取RouteDefinition
* 5.RouteDefinitionRepository-从存储器( 例如,内存 / Redis / MySQL 等 )读取RouteDefinition
* @author Spencer Gibb
*/
public interface RouteDefinitionLocator {
/**
* 获取RouteDefinition
* @return
*/
Flux<RouteDefinition> getRouteDefinitions();
}
RouteDefinitionLocator接口有且仅有一个方法getRouteDefinitions,此方法获取RouteDefinition的核心方法,返回Flux
RouteDefinitionLocator 类图如下:
graph TD
RouteDefinitionLocator-->CachingRouteDefinitionLocator
RouteDefinitionLocator-->CompositeRouteDefinitionLocator
RouteDefinitionLocator-->PropertiesRouteDefinitionLocator
RouteDefinitionLocator-->DiscoveryClientRouteDefinitionLocator
RouteDefinitionLocator-->RouteDefinitionRepository
子类功能描述:
- CachingRouteDefinitionLocator:RouteDefinitionLocator包装类, 缓存目标RouteDefinitionLocator 为routeDefinitions提供缓存功能
- CompositeRouteDefinitionLocator -RouteDefinitionLocator包装类,组合多种 RouteDefinitionLocator 的实现,为 routeDefinitions提供统一入口
- PropertiesRouteDefinitionLocator-从配置文件(GatewayProperties 例如,YML / Properties 等 ) 读取RouteDefinition
- RouteDefinitionRepository-从存储器( 例如,内存 / Redis / MySQL 等 )读取RouteDefinition
- DiscoveryClientRouteDefinitionLocator-从注册中心( 例如,Eureka / Consul / Zookeeper / Etcd 等
- PropertiesRouteDefinitionLocator
/**
* 从Properties(GatewayProperties)中加载RouteDefinition信息
* @author Spencer Gibb
*/
public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
/**
* 从appliccation.yml中解析前缀为spring.cloud.gateway的配置
*/
private final GatewayProperties properties;
public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
this.properties = properties;
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.properties.getRoutes());
}
}
PropertiesRouteDefinitionLocator很简单从GatewayProperties实例获取RouteDefinition信息
- GatewayProperties 在GatewayProperties初始化加载文中已详细描述
- Flux 响应式编程
- CachingRouteDefinitionLocator
/**
* RouteDefinitionLocator 包装实现类,实现了路由定义的本地缓存功能
* @author Spencer Gibb
*/
public class CachingRouteDefinitionLocator implements RouteDefinitionLocator {
/**
* 实际路由定义定位器
*/
private final RouteDefinitionLocator delegate;
private final Flux<RouteDefinition> routeDefinitions;
/**
* 路由定义的本地缓存
*/
private final Map<String, List> cache = new HashMap<>();
public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {
this.delegate = delegate;
routeDefinitions = CacheFlux.lookup(cache, "routeDefs", RouteDefinition.class)
.onCacheMissResume(() -> this.delegate.getRouteDefinitions());
}
}
RouteDefinitionLocator包装类,缓存目标RouteDefinitionLocator 为routeDefinitions提供缓存功能
- DiscoveryClientRouteDefinitionLocator
public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
/**
* 注册中心客户端
*/
private final DiscoveryClient discoveryClient;
/**
* 本地配置信息
*/
private final DiscoveryLocatorProperties properties;
/**
* 路由ID前缀
*/
private final String routeIdPrefix;
public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
this.discoveryClient = discoveryClient;
this.properties = properties;
if (StringUtils.hasText(properties.getRouteIdPrefix())) {
this.routeIdPrefix = properties.getRouteIdPrefix();
} else {
this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_";
}
}
/**
* 通过注册中心查找服务组装路由定义信息
* @return
*/
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
...代码在根据注册中心查找路由详细解析
}
}
DiscoveryClientRouteDefinitionLocator通过调用 DiscoveryClient 获取注册在注册中心的服务列表,生成对应的 RouteDefinition 数组
- CompositeRouteDefinitionLocator
/**
* 组合多个 RouteDefinitionLocator 的实现,为 routeDefinitions提供统一入口
* @author Spencer Gibb
*/
public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
/**
* 所有路由定义定位器实例集合
*/
private final Flux<RouteDefinitionLocator> delegates;
public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates) {
this.delegates = delegates;
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
//将各个RouteDefinitionLocator的getRouteDefinitions合并返回统一的Flux<RouteDefinition>
return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
}
}
CompositeRouteDefinitionLocator 的主要作用就是将各个定位器合并提供统一的getRouteDefinitions方法入口
通过子类实现具体功能可以很清晰的看到定位器加载RouteDefinition整个流程
graph TD
PropertiesRouteDefinitionLocator-->|配置文件加载初始化| CompositeRouteDefinitionLocator
RouteDefinitionRepository-->|存储器中加载初始化| CompositeRouteDefinitionLocator
DiscoveryClientRouteDefinitionLocator-->|注册中心加载初始化| CompositeRouteDefinitionLocator
最终提供通过CompositeRouteDefinitionLocator提供统一的 getRouteDefinitions方法
RouteDefinitionLocator实例的初始化在GatewayAutoConfiguration中已经完成
- GatewayDiscoveryClientAutoConfiguration
@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@AutoConfigureBefore(GatewayAutoConfiguration.class)
@ConditionalOnClass({DispatcherHandler.class, DiscoveryClient.class})
@EnableConfigurationProperties
public class GatewayDiscoveryClientAutoConfiguration {
//初始化注册中心路由定义定位器
@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(
DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
}
- GatewayAutoConfiguration
//初始化配置路由定义加载器
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
return new PropertiesRouteDefinitionLocator(properties);
}
//初始化存储路由定义加载器
@Bean
@ConditionalOnMissingBean(RouteDefinitionRepository.class)
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();
}
//初始化聚合路由定义加载器
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
}
在Spring-Cloud-Gateway初始化完成后需要的路由定义加载器已全部实例化完成,这样就为路由的加载创建完成了必要的条件。