这是Spring cloud gateway 的架构图,层次清晰,需要详细看的话可以直接全局搜索查看代码,里面用到了 Reactor 3 和 netty。
它和Spring Mvc看上去流程是相似的,Spring Mvc 是在 DispatchServlet 中获取 Handler,执行拦截器链,根据 Handler 获取 HandlerAdapter,然后解析请求参数,反射调用 Controller Method,得到执行结果,然后包装响应结果,直接返回 。而 gateway 走的是 Spring Webflux 的执行流程。
下面这是 Spring Webflux的核心,DispatcherHandler 请求分发器,handlerMappings,handlerAdapters,resultHandlers,这几乎和spring mvc是一样的,它会从handlerMappings中获得匹配的Route,再执行 invokeHandler(exchange, handler),它会选择走 SimpleHandlerAdapter的 handle方法,进入 FilteringWebHandler 类的 handle方法执行过滤器链,获取具体的微服务执行结果,返回结果。注意过滤器分为pre和post两种类型。 当一个请求到达网关被处理,它就是被 Spring Webflux 所处理,这个请求就是一个普通的请求,这就是网关的内部流程。
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
@Nullable
private List handlerMappings;
@Nullable
private List handlerAdapters;
@Nullable
private List resultHandlers;
...
@Override
public Mono 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));
}
private Mono 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));
}
}
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
discovery:
locator:
enabled: true
routes是RouteDefinition,是路由的元信息,后面会组装为Route实例。就像Spring 里的BeanDefinition最终会实例化为一个Bean一样。Route里有一个异步predicate ,它会把Route里的所有普通 predicate 组合为一个 很长的异步predicate,Route里还有许多自定义的过滤器,需要自己配置。
在 this.routeLocator.getRoutes() 的返回值是 Flux
下图就是做路由匹配的过程,我们的 只配置了 Path=/user/**,所以就进入 PathRoutePredicateFactory 的判断逻辑,不同的断言进入不同的判断逻辑。
如果我们加了另一个断言 - After=2022-08-01T15:59:59Z[Asia/Shanghai], 它就会进入到一个异步断言中去,执行所有断言,并把它们的结果做一个 “与” 运算。
public interface AsyncPredicate extends Function>
@Override
public Publisher apply(T t) {
return Flux.zip(left.apply(t), right.apply(t))
.map(tuple -> tuple.getT1() && tuple.getT2());
}
}
开始执行过滤器链。
责任链模式,就是遍历一个 List
private static class DefaultGatewayFilterChain implements GatewayFilterChain {
private final int index;
private final List filters;
DefaultGatewayFilterChain(List filters) {
this.filters = filters;
this.index = 0;
}
private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
this.filters = parent.getFilters();
this.index = index;
}
public List getFilters() {
return filters;
}
@Override
public Mono 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
}
});
}
}
Gateway里有两种 Filter,一种是全局过滤器 GlobalFilter,作用在每一个 Route 里,另一种是局部过滤器 GatewayFilter,作用在某个 Route 或一组 Route 上,这时候就使用到适配器模式了。
public interface GlobalFilter {
Mono filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
public interface GatewayFilter extends ShortcutConfigurable {
String NAME_KEY = "name";
String VALUE_KEY = "value";
Mono filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
可以看到 GlobalFilter 和 GatewayFilter 都是函数式接口,而且接口签名都一样。
private static class GatewayFilterAdapter implements GatewayFilter {
private final GlobalFilter delegate;
GatewayFilterAdapter(GlobalFilter delegate) {
this.delegate = delegate;
}
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
}
看得出,GatewayFilterAdapter 持有一个 GlobalFilter,但它会把这个GlobalFilter当成一个GatewayFilter 来使用,这就是适配器模式。
在loadFilters()这里完成全部的适配器转换。
private static List loadFilters(List filters) {
return filters.stream().map(filter -> {
GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
if (filter instanceof Ordered) {
int order = ((Ordered) filter).getOrder();
return new OrderedGatewayFilter(gatewayFilter, order);
}
return gatewayFilter;
}).collect(Collectors.toList());
}
开始执行过滤器链,这时候会根据 lb://user-service 从注册中心获取到一个服务实例的ip和端口,这样就可以构造http请求,路由到具体的微服务实例去了,这时候用到的过滤器就是 LoadBalancerClientFilter 。
具体选择哪个服务实例,主要是由Ribbon来做的,默认是 RoundRobin ,即轮询。
构造好 http 请求后它会进入到 NettyRoutingFilter 过滤器,它会发送请求,并获得请求的输出结果,这里会使用到 netty 发送请求。这时候有点像 Open Feign,把构造好的 http 请求发送过去,然后接收响应。
public class NettyRoutingFilter implements GlobalFilter, Ordered {
private static final Log log = LogFactory.getLog(NettyRoutingFilter.class);
private final HttpClient httpClient;
...
@Override
@SuppressWarnings("Duplicates")
public Mono 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 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) -> {
if (log.isTraceEnabled()) {
nettyOutbound
.withConnection(connection -> log.trace("outbound route: "
+ connection.channel().id().asShortText()
+ ", inbound: " + exchange.getLogPrefix()));
}
return nettyOutbound.send(request.getBody()
.map(dataBuffer -> ((NettyDataBuffer) dataBuffer)
.getNativeBuffer()));
}).responseConnection((res, connection) -> {
// Defer committing the response until all route filters have run
// Put client response as ServerWebExchange attribute and write
// response later NettyWriteResponseFilter
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
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);
// make sure headers filters run after setting status so it is
// available in response
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
getHeadersFilters(), headers, exchange, Type.RESPONSE);
if (!filteredResponseHeaders
.containsKey(HttpHeaders.TRANSFER_ENCODING)
&& filteredResponseHeaders
.containsKey(HttpHeaders.CONTENT_LENGTH)) {
// It is not valid to have both the transfer-encoding header and
// the content-length header.
// Remove the transfer-encoding header in the response if the
// content-length header is present.
response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);
}
exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES,
filteredResponseHeaders.keySet());
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));
}
}
再往后按理说它会进入到 DispatcherHandler 执行结果处理,但 debug 没进来,在过滤器链里执行完,获取到结果后它就结束了。
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());
}
GlobalFilter里有很多很丰富的过滤器,比如 RouteToRequestUrlFilter 可以将我们的请求前面的域名和端口转换成我们要调用的服务,并且判断了是否需要进行负载均衡。WebsocketRoutingFilter 可以处理"ws://","wss://"等 websocket协议。RequestRateLimiterGatewayFilter 可以处理请求限流。 HystrixGatewayFilter 可以处理熔断。 这就是网关各种功能的由来。
未完待续。。。