目录
3.3. RoutePredicateHandlerMapping
6.3. GatewayFilterAdapter-适配器方式
Versoin Spring Cloud Gateway 3.0.0-SNAPSHOT
This project provides an API Gateway built on top of the Spring Ecosystem, including: Spring 5, Spring Boot 2 and Project Reactor. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.
该项目提供了一个构建在 Spring 生态系统之上的 API 网关,包括:Spring 6、Spring Boot 3 和 Project Reactor。Spring Cloud Gateway 旨在提供一种简单而有效的方法来路由到 API,并为它们提供横切关注点,例如:安全性、监控/指标和弹性。
To include Spring Cloud Gateway in your project, use the starter with a group ID of org.springframework.cloud and an artifact ID of spring-cloud-starter-gateway. See the Spring Cloud Project page for details on setting up your build system with the current Spring Cloud Release Train.
要在项目中包含SpringCloudGateway,请使用组ID为org.springframework.Cloud的starter和工件ID为Springcloudstartergateway的starter。有关使用当前SpringCloudRelease Train设置构建系统的详细信息,请参阅SpringCloudProject页面。
Glossary
术语汇编
Route: The basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates, and a collection of filters. A route is matched if the aggregate predicate is true.
路线:网关的基本组成部分。它由ID、目标URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则匹配路由。
Predicate: This is a Java 8 Function Predicate. The input type is a Spring Framework ServerWebExchange. This lets you match on anything from the HTTP request, such as headers or parameters.
谓词:这是一个Java8函数谓词。输入类型是Spring Framework ServerWebExchange。这允许您匹配HTTP请求中的任何内容,例如标头或参数。
Filter: These are instances of Spring Framework GatewayFilter that have been constructed with a specific factory. Here, you can modify requests and responses before or after sending the downstream request.
筛选器:这些是使用特定工厂构建的Spring Framework GatewayFilter的实例。在这里,您可以在发送下游请求之前或之后修改请求和响应。
提供了 Spring Cloud Gateway 工作原理的概述:
Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a route, it is sent to the Gateway Web Handler. This handler runs the request through a filter chain that is specific to the request. The reason the filters are divided by the dotted line is that filters can run logic both before and after the proxy request is sent. All “pre” filter logic is executed. Then the proxy request is made. After the proxy request is made, the “post” filter logic is run.
客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 确定请求与路由匹配,则将其发送到 Gateway Web Handler。此处理程序通过特定于请求的过滤器链运行请求。过滤器被虚线分开的原因是过滤器可以在发送代理请求之前和之后运行逻辑。执行所有“预”过滤器逻辑。然后进行代理请求。发出代理请求后,运行“post”过滤器逻辑。
org.springframework.cloud.gateway.config. GatewayAutoConfiguration通过注解加载启动
@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class })
@AutoConfigureAfter({ GatewayLoadBalancerClientAutoConfiguration.class,
GatewayClassPathWarningAutoConfiguration.class })
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
…
}
org.springframework.cloud.gateway.config. GatewayAutoConfiguration通过注解初始化各种factory
@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();
}
org.springframework.cloud.gateway.config. GatewayAutoConfiguration通过注解初始化各种handler
@Bean
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
return new FilteringWebHandler(globalFilters);
}
org.springframework.cloud.gateway.config. GatewayAutoConfiguration通过注解初始化各种Listener
@Bean
@Primary
//TODO: property to disable composite?
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
@Bean
public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {
return new RouteRefreshListener(publisher);
}
org.springframework.web.reactive.config. WebFluxConfigurationSupport注解启动加载DispatcherHandler
@Bean
public DispatcherHandler webHandler() {
return new DispatcherHandler();
}
org.springframework.web.reactive.config.DispatcherHandler初始化加载所有的HandlerMapping
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);
}
org.springframework.web.reactive.config.DispatcherHandler的handle方法通过HandlerMapping找到handler
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
return Flux.fromIterable(this.handlerMappings)
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
.switchIfEmpty(createNotFoundError())
.flatMap(handler -> invokeHandler(exchange, handler))
.flatMap(result -> handleResult(exchange, result));
}
org.springframework.cloud.gateway.handler. RoutePredicateHandlerMapping注解启动加载getHandlerInternal与lookupRoute找到Route
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
// don't handle requests on the management port if set
if (managmentPort != null && exchange.getRequest().getURI().getPort() == managmentPort.intValue()) {
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) + "]");
}
})));
}
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());
}*/
}
通过各个RoutePredicateFactory判断路由
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
List<HttpCookie> cookies = exchange.getRequest().getCookies().get(config.name);
if (cookies == null) {
return false;
}
for (HttpCookie cookie : cookies) {
if (cookie.getValue().matches(config.regexp)) {
return true;
}
}
return false;
};
}
Route:网关的基本构建块。它由 ID、目标 URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则路由匹配。
Predicate:这是一个Java 8 函数谓词。输入类型是Spring FrameworkServerWebExchange。这使您可以匹配 HTTP 请求中的任何内容,例如标头或参数。
Filter : Spring Framework GatewayFilter厂构建的实例。在这里,您可以在发送下游请求之前或之后修改请求和响应。
org.springframework.cloud.gateway.route. Route内容
public class Route implements Ordered {
private final String id;
private final URI uri;
private final int order;
private final AsyncPredicate<ServerWebExchange> predicate;
private final List<GatewayFilter> gatewayFilters;
}
org.springframework.cloud.gateway.route. RouteDefinition内容
public class RouteDefinition {
@NotEmpty
private String id = UUID.randomUUID().toString();
@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList<>();
@Valid
private List<FilterDefinition> filters = new ArrayList<>();
@NotNull
private URI uri;
private int order = 0;
}
org.springframework.cloud.gateway.route. CachingRouteLocator缓存对象
public class CachingRouteLocator implements RouteLocator, ApplicationListener<RefreshRoutesEvent> {
private final RouteLocator delegate;
private final Flux<Route> routes;
private final Map<String, List> cache = new HashMap<>();
public CachingRouteLocator(RouteLocator delegate) {
this.delegate = delegate;
routes = CacheFlux.lookup(cache, "routes", Route.class)
.onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE));
}
}
org.springframework.cloud.gateway.route. InMemoryRouteDefinitionRepository缓存对象
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap( r -> {
routes.put(r.getId(), r);
return Mono.empty();
});
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id -> {
if (routes.containsKey(id)) {
routes.remove(id);
return Mono.empty();
}
return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: "+routeId)));
});
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(routes.values());
}
}
org.springframework.cloud.gateway.route. RouteRefreshListener 动态路由实现
public class RouteRefreshListener
implements ApplicationListener<ApplicationEvent> {
private HeartbeatMonitor monitor = new HeartbeatMonitor();
private final ApplicationEventPublisher publisher;
public RouteRefreshListener(ApplicationEventPublisher publisher) {
Assert.notNull(publisher, "publisher may not be null");
this.publisher = publisher;
}
}
Route:网关的基本构建块。它由 ID、目标 URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则路由匹配。
Predicate:这是一个Java 8 函数谓词。输入类型是Spring FrameworkServerWebExchange。这使您可以匹配 HTTP 请求中的任何内容,例如标头或参数。
public class DefaultServerWebExchange implements ServerWebExchange {
private final ServerHttpRequest request;
private final ServerHttpResponse response;
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
private final Mono<WebSession> sessionMono;
private final LocaleContextResolver localeContextResolver;
private final Mono<MultiValueMap<String, String>> formDataMono;
private final Mono<MultiValueMap<String, Part>> multipartDataMono;
@Nullable
private final ApplicationContext applicationContext;
}
Route:网关的基本构建块。它由 ID、目标 URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则路由匹配。
Predicate:这是一个Java 8 函数谓词。输入类型是Spring FrameworkServerWebExchange。这使您可以匹配 HTTP 请求中的任何内容,例如标头或参数。
The After Route Predicate Factory --时间:匹配之前
The Before Route Predicate Factory --时间:匹配之后
The Between Route Predicate Factory --匹配两个时间之间
The Cookie Route Predicate Factory --匹配cookie内容
The Header Route Predicate Factory --匹配Header 内容
The Host Route Predicate Factory--匹配Host 内容
The Method Route Predicate Factory--匹配Host 内容
The Path Route Predicate Factory --匹配Path 内容
The Query Route Predicate Factory -- 匹配参数
The RemoteAddr Route Predicate Factory --匹配远程地址
The Weight Route Predicate Factory --配置权重路由谓
org.springframework.cloud.gateway.handler.predicate .CookieRoutePredicateFactory例子
public class CookieRoutePredicateFactory extends AbstractRoutePredicateFactory<CookieRoutePredicateFactory.Config> {
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
List<HttpCookie> cookies = exchange.getRequest().getCookies().get(config.name);
if (cookies == null) {
return false;
}
for (HttpCookie cookie : cookies) {
if (cookie.getValue().matches(config.regexp)) {
return true;
}
}
return false;
};
}
org.springframework.cloud.gateway.filter. GlobalFilter全局过滤器
public interface GlobalFilter {
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
。
org.springframework.cloud.gateway.filter. GatewayFilter局部过滤器
public interface GatewayFilter extends ShortcutConfigurable {
String NAME_KEY = "name";
String VALUE_KEY = "value";
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
org.springframework.cloud.gateway. handler. FilteringWebHandler. GatewayFilterAdapter局部过滤器
private static class GatewayFilterAdapter implements GatewayFilter {
private final GlobalFilter delegate;
public GatewayFilterAdapter(GlobalFilter delegate) {
this.delegate = delegate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
sb.append("delegate=").append(delegate);
sb.append('}');
return sb.toString();
}
}
org.springframework.cloud.gateway.route.builder. GatewayFilterSpec局部过滤器
public GatewayFilterSpec addRequestHeader(String headerName, String headerValue) {
return filter(getBean(AddRequestHeaderGatewayFilterFactory.class)
.apply(c -> c.setName(headerName).setValue(headerValue)));
}
Step 1 : RibbonAutoConfiguration自动配置
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
RibbonClientConfiguration 自动配置(默认)
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
return this.propertiesFactory.get(ILoadBalancer.class, config, name);
}
return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}
BestAvailableRule:选择最小请求数的Server
RandomRule:随机选择Server
RoundRobinRule:轮询选择Server
RetryRule:根据轮询的方式重试
WeightedResponseTimeRule:根据响应时间去分配Weight,Weight越高,被选择的可能性就越大
ZoneAvoidanceRule:根据Server的zone区域和可用性来轮询选择,如果只有一个zone区域,行为跟RoundRobinRule一致
AvailabilityFilteringRule:过滤掉那些因为一直连接失败(标记了circuit tripped)和高并发(activeConnections 超过配置的阈值)的Server,尽量保证可用性
DynamicServerListLoadBalancer:继承于BaseLoadBalancer,它对基础负载均衡器做了扩展。在该负载均衡器,实现了服务实例清单在运行期间的动态更新;同时还具备了对服务实例清单的过滤功能,也就是说可以通过过滤器来选择获取一批服务实例清单。
ZoneAwareLoadBalancer:对DynamicServerListLoadBalancer的扩展。在DynamicServerListLoadBalancer中没有区分zone区域,而ZoneAwareLoadBalancer主要扩展的功能就是增加了zone区域过滤。
Step 2 :LoadBalancerClientFilter的filter方法,判断LB负载均衡。
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
return chain.filter(exchange);
}
//preserve the original url
addOriginalRequestUrl(exchange, url);
log.trace("LoadBalancerClientFilter url before: " + url);
final ServiceInstance instance = choose(exchange);
if (instance == null) {
String msg = "Unable to find instance for " + url.getHost();
if(properties.isUse404()) {
throw new FourOFourNotFoundException(msg);
}
throw new NotFoundException(msg);
}
URI uri = exchange.getRequest().getURI();
// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
// if the loadbalancer doesn't provide one.
String overrideScheme = instance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange);
}
Step 3: 通过LoadBalancerClient获得真是访问地址
final ServiceInstance instance = choose(exchange);
Step 4:通过RibbonLoadBalancerClient的choose方法
/**
* New: Select a server using a 'key'.
*/
public ServiceInstance choose(String serviceId, Object hint) {
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
}
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}
Step 5:通过ILoadBalancer 的获取默认算法ZoneAwareLoadBalancer的负载均衡
具体算法参考代码:
@Override
protected void setServerListForZones(Map<String, List<Server>> zoneServersMap) {
super.setServerListForZones(zoneServersMap);
if (balancers == null) {
balancers = new ConcurrentHashMap<String, BaseLoadBalancer>();
}
for (Map.Entry<String, List<Server>> entry: zoneServersMap.entrySet()) {
String zone = entry.getKey().toLowerCase();
getLoadBalancer(zone).setServersList(entry.getValue());
}
// check if there is any zone that no longer has a server
// and set the list to empty so that the zone related metrics does not
// contain stale data
for (Map.Entry<String, BaseLoadBalancer> existingLBEntry: balancers.entrySet()) {
if (!zoneServersMap.keySet().contains(existingLBEntry.getKey())) {
existingLBEntry.getValue().setServersList(Collections.emptyList());
}
}
}
。
spring-cloud-gateway官网
https://cloud.spring.io/spring-cloud-gateway/reference/html/
Spring-Cloud-Gateway+Ribbon负载均衡
https://blog.csdn.net/htmlxxxx/article/details/115379226