核心概念:
- 路由:基础部分,路由信息包括路由ID、目标uri、一组断言和一组过滤器组成。如果断言为真,则说明请求的url和配置匹配。
- 断言:Java8种断言函数。
- 过滤器:Gateway Filter & Global Filter。
自动配置类GatewayAutoConfiguration在内部初始化了很多bean,列举几个重要的如下:
- PropertiesRouteDefinitionLocator:用于从配置文件(yml/properties)中读取路由配置信息。
- RouteDefinitionLocator:把 RouteDefinition 转化为 Route。
- RoutePredicateHandlerMapping:类似于 mvc 的HandlerMapping,不过这里是Gateway实现的。用于匹配对应的请求route。 GatewayProperties:yml配置信息封装在GatewayProperties 对象中。
- AfterRoutePredicateFactory:各种路由断言工厂,正是这些断言工厂在启动时已经生成对应的bean,我们才可以在yml 中配置一下,即可生效。
- RetryGatewayFilterFactory:各种 Gateway过滤器,正是这些过滤器在启动时已经生成对应的bean,我们才可以在 yml 中配置一下,即可生效。
- GlobalFilter实现类:全局过滤器。
本文大概:
- handlerMapping通过
Route
获取的Predicate断言匹配路由。 - 匹配成功之后将 Route 被
ServerWebExchange
持有。 - 通过handlerMapping获取处理器handler。
- handler通过Route获取gatewayFilter。
- handler获取全局过滤器。
- 递归执行全部的过滤器。
1、HttpServerHandle
Netty获取到客户端连接后,开始获取handler。文章从 HttpServerHandle
开始解析HTTP请求。
public void onStateChange(Connection connection, State newState) {
// ops:请求 & 响应
HttpServerOperations ops = (HttpServerOperations) connection;
//handler:ReactorHttpHandlerAdapter
Mono.fromDirect(handler.apply(ops, ops))
.subscribe(ops.disposeSubscriber());
}
1.1、ReactorHttpHandlerAdapter
public Mono<Void> apply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) {
ReactorServerHttpRequest request = new ReactorServerHttpRequest(reactorRequest, bufferFactory);
ServerHttpResponse response = new ReactorServerHttpResponse(reactorResponse, bufferFactory);
// ReactiveWebServerApplicationContext
return this.httpHandler.handle(request, response)
.doOnError(ex -> logger.trace(request.getLogPrefix() + "Failed to complete: " + ex.getMessage()))
.doOnSuccess(aVoid -> logger.trace(request.getLogPrefix() + "Handling completed"));
}
1.2、HttpWebHandlerAdapter
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
if (this.forwardedHeaderTransformer != null) {
request = this.forwardedHeaderTransformer.apply(request);
}
ServerWebExchange exchange = createExchange(request, response);
// ExceptionHandlingWebHandler#handle
return getDelegate().handle(exchange)
.doOnSuccess(aVoid -> logResponse(exchange))
.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
.then(Mono.defer(response::setComplete));
}
1.3、FilteringWebHandler
public Mono<Void> handle(ServerWebExchange exchange) {
// DefaultWebFilterChain
return this.chain.filter(exchange);
}
1.4、DefaultWebFilterChain
该filter方法存在递归调用。订阅者与发布者之间建立订阅关系后,首次调用执行invokeFilter其返回的还是发布者defer,根据引用得知,流式处理中会再次调用defer中的函数式接口Supplier默认方法,此时执行DispatcherHandler。
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.defer(() ->
// 初始化的过滤器 WeightCalculatorWebFilter
this.currentFilter != null && this.chain != null ?
invokeFilter(this.currentFilter, this.chain, exchange) :
// DispatcherHandler
this.handler.handle(exchange));
}
- DefaultWebFilterChain 属性中包含自身引用的属性chain。实例化过程中除了实例化本身之外,还会初始化其自身属性chain。此时的属性chain是没有初始化其自身的属性chain以及handler。
- 响应式规范下的Java 框架reactor库中,对于发布者defer其存在嵌套调用的情况。【源码比较直观】
HttpServerHandle的主要作用是得到DispatcherHandler
。
2、DispatcherHandler
2.1、通过handlerMapping获取handler
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
return Flux.fromIterable(this.handlerMappings)
// RoutePredicateHandlerMapping匹配路由 & 跨域处理
.concatMap(mapping -> mapping.getHandler(exchange))
.next()//
.switchIfEmpty(createNotFoundError())// 如果路由匹配失败,也就是返回Mono.empty,则直接抛出404异常
// 路由匹配成功后返回Handler,FilteringWebHandler
.flatMap(handler -> invokeHandler(exchange, handler))
.flatMap(result -> handleResult(exchange, result));
}
handlerMappings:
- RouterFunctionMapping。
- RequestMappingHandlerMapping。
- RoutePredicateHandlerMapping【路由匹配】。#3
- SimpleUrlHandlerMapping。
2.2、Handler适配器
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));
}
- RequestMappingHandlerAdapter。
- HandlerFunctionAdapter。
- SimpleHandlerAdapter【支持】。
2.3、SimpleHandlerAdapter适配器处理Filter
通过适配器获取并执行全部过滤器。
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
WebHandler webHandler = (WebHandler) handler;
// FilteringWebHandler
Mono<Void> mono = webHandler.handle(exchange);
return mono.then(Mono.empty());
}
FilteringWebHandler#handle
public Mono<Void> handle(ServerWebExchange exchange) {
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
//获取自定义的过滤器
List<GatewayFilter> gatewayFilters = route.getFilters();
//获取全部的全局过滤器
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
combined.addAll(gatewayFilters);
// TODO: needed or cached?
AnnotationAwareOrderComparator.sort(combined);
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
- 从Route获取GatewayFilter。其中就包括配置routes下的filters选项。【限流相关的过滤器参考】
- 获取全局过滤器。
- GatewayFilterFactory默认情况会初始化30种类型的过滤器工厂类,其创建的GatewayFilter均是匿名内部类方式。DefaultGatewayFilterChain在执行GatewayFilter时会回调匿名内部类过滤逻辑。
限流效果:
3、RoutePredicateHandlerMapping路由匹配
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
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)
.flatMap((Function<Route, Mono<?>>) r -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
// 路由匹配通过之后,将 Route 对象作为ServerWebExchange的GATEWAY_ROUTE_ATTR属性。
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
return Mono.just(webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
})));
}
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
//routeLocator:CachingRouteLocator
return this.routeLocator.getRoutes()
.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);
})
.doOnError(e -> logger.error(
"Error applying predicate for route: " + route.getId(),
e))
.onErrorResume(e -> Mono.empty()))
.next()
// TODO: error handling
.map(route -> {
validateRoute(route, exchange);
return route;
});
}
- Predicate断言 & Filter 均是有
Route
持有。 - 通过 Route 获取到Predicate断言,执行路由的匹配。
- Route 对象被ServerWebExchange持有。【目的是执行过滤器功能】
4、过滤器
过滤器主要分为两种:GatewayFilter & GlobalFilter。
4.1、GlobalFilter
全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
过滤器order值越小优先级越高:
- RemoveCachedBodyFilter。
- AdaptCachedBodyGlobalFilter。
- NettyWriteResponseFilter。
- ForwardPathFilter:处理重定向。
- RouteToRequestUrlFilter。
- LoadBalancerClientFilter。
- WebsocketRoutingFilter:处理webSocket类型的请求。
- NettyRoutingFilter:发送HTTP请求。
- ForwardRoutingFilter。
private static class DefaultGatewayFilterChain implements GatewayFilterChain {
private final int index;
private final List<GatewayFilter> filters;
DefaultGatewayFilterChain(List<GatewayFilter> filters) {
this.filters = filters;
this.index = 0;
}
private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
this.filters = parent.getFilters();
this.index = index;
}
public List<GatewayFilter> getFilters() {
return filters;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.defer(() -> {
if (this.index < filters.size()) {
// 全部过滤器:自定义 & 全局过滤器
GatewayFilter filter = filters.get(this.index);
DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this,
this.index + 1);
return filter.filter(exchange, chain);
}
else {
return Mono.empty(); // complete
}
});
}
}
4.1.1、NettyWriteResponseFilter
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange)
// 这个cleanup()的作用是关闭连接,此处的意思是出错时关闭response connection
.doOnError(throwable -> cleanup(exchange))
.then(Mono.defer(() -> {
// 1.从exchange拿到response connection,它是被NettyRoutingFilter发请求后塞进去的
Connection connection = exchange.getAttribute(CLIENT_RESPONSE_CONN_ATTR);
if (connection == null) {
return Mono.empty();
}
// 2.从connection接收字节流写入response
ServerHttpResponse response = exchange.getResponse();
NettyDataBufferFactory factory = (NettyDataBufferFactory) response
.bufferFactory();
final Flux<NettyDataBuffer> body = connection
.inbound()
.receive()
.retain()
.map(factory::wrap);
MediaType contentType = response.getHeaders().getContentType();
// 3.针对response是否为流媒体内容采取不同的返回调用,最终就返回给请求方了
return (isStreamingMediaType(contentType)
? response.writeAndFlushWith(body.map(Flux::just))
: response.writeWith(body));
})).doOnCancel(() -> cleanup(exchange));
// @formatter:on
}
4.1.2、RouteToRequestUrlFilter
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
if (route == null) {
return chain.filter(exchange);
}
URI uri = exchange.getRequest().getURI();
boolean encoded = containsEncodedParts(uri);
URI routeUri = route.getUri();
if (hasAnotherScheme(routeUri)) {
exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR,
routeUri.getScheme());
routeUri = URI.create(routeUri.getSchemeSpecificPart());
}
if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {
throw new IllegalStateException("Invalid host: " + routeUri.toString());
}
URI mergedUrl = UriComponentsBuilder.fromUri(uri)
// .uri(routeUri)
.scheme(routeUri.getScheme()).host(routeUri.getHost())
.port(routeUri.getPort()).build(encoded).toUri();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
return chain.filter(exchange);
}
- uri:http://localhost:8096/gw/3?token=1。
- mergedUrl:lb:provider-service/gw/3?token=1。
- GATEWAY_REQUEST_URL_ATTR:gatewayRequestUrl。
4.1.3、LoadBalancerClientFilter
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);//lb:provider-service/gw/3?token=1
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
// preserve the original url
addOriginalRequestUrl(exchange, url);
// 通过Ribbon客户端负载均衡获取目标服务IP、port等信息
final ServiceInstance instance = choose(exchange);
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();
}
// 通过负载均衡策略获取到目标服务的某个IP & 端口,拼接成为目标URI
URI requestUrl = loadBalancer.reconstructURI(
new DelegatingServiceInstance(instance, overrideScheme), uri);
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange);
}
protected ServiceInstance choose(ServerWebExchange exchange) {
//RibbonLoadBalancerClient
return loadBalancer.choose(
((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost());
}
4.1.4、NettyRoutingFilter
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
String scheme = requestUrl.getScheme();
//如果请求已经被标记为已经被路由过了
if (isAlreadyRouted(exchange)
|| (!"http".equals(scheme) && !"https".equals(scheme))) {
return chain.filter(exchange);
}
// 否则就标记为已经被路由过了,避免被其他路由处理
setAlreadyRouted(exchange);
ServerHttpRequest request = exchange.getRequest();
final HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
final String url = requestUrl.toASCIIString();
HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);
final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
filtered.forEach(httpHeaders::set);
boolean preserveHost = exchange
.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
Flux<HttpClientResponse> responseFlux = httpClientWithTimeoutFrom(route)//设置超时时间
.headers(headers -> {
headers.add(httpHeaders);
// Will either be set below, or later by Netty
headers.remove(HttpHeaders.HOST);
if (preserveHost) {
String host = request.getHeaders().getFirst(HttpHeaders.HOST);
headers.add(HttpHeaders.HOST, host);
}
}).request(method).uri(url).send((req, nettyOutbound) -> {//url:目标地址
return nettyOutbound.send(request.getBody()
.map(dataBuffer -> ((NettyDataBuffer) dataBuffer)
.getNativeBuffer()));
}).responseConnection((res, connection) -> {
// 4.配置response connection
// 之后NettyWriteResponseFilter就是靠这个连接来接收response字节流
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
// 把response connection设置为一个属性到exchange
// 这个版本已经变成NettyWriteResponseFilter通过response connection拿到响应字节流的
exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);
ServerHttpResponse response = exchange.getResponse();
// put headers and status so filters can modify the response
HttpHeaders headers = new HttpHeaders();
res.responseHeaders().forEach(
entry -> headers.add(entry.getKey(), entry.getValue()));
String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
if (StringUtils.hasLength(contentTypeValue)) {
exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR,
contentTypeValue);
}
setResponseStatus(res, response);
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
getHeadersFilters(), headers, exchange, Type.RESPONSE);
...
response.getHeaders().putAll(filteredResponseHeaders);
return Mono.just(res);
});
Duration responseTimeout = getResponseTimeout(route);
if (responseTimeout != null) {
responseFlux = responseFlux
.timeout(responseTimeout, Mono.error(new TimeoutException(
"Response took longer than timeout: " + responseTimeout)))
.onErrorMap(TimeoutException.class,
th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT,
th.getMessage(), th));
}
return responseFlux.then(chain.filter(exchange));
}
private HttpClient httpClientWithTimeoutFrom(Route route) {
Integer connectTimeout = (Integer) route.getMetadata().get(CONNECT_TIMEOUT_ATTR);
if (connectTimeout != null) {
return this.httpClient.tcpConfiguration((tcpClient) -> tcpClient
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout));
}
//HttpClientTcpConfig
return httpClient;
}
4.2、GatewayFilter
需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上。
RequestRateLimiter就是官方定义的默认31种过滤器之一。
5、predicate
时间类型的Predicated(AfterRoutePredicateFactory BeforeRoutePredicateFactory BetweenRoutePredicateFactory),当只有满足特定时间要求的请求会进入到此predicate中,并交由router处理;cookie类型的CookieRoutePredicateFactory,指定的cookie满足正则匹配,才会进入此router;以及host、method、path、querparam、remoteaddr类型的predicate,每一种predicate都会对当前的客户端请求进行判断,是否满足当前的要求,如果满足则交给当前请求处理。