springcloud 提供了便于我们编写网关组件,分别是zuul和gateway;在zuul1的通信模型是BIO而zuul2的通信模型采用了NIO;spring gateway的IO模型使用的是NIO;但是从netflix发布zuul2后spring已经 开始 不集成zuul组件了。spring gateway的架构是基于WebFlux基础开发的。它依赖springboot2.x,spring5和netty
在spring MVC是通过handlermapping解析请求连接然后根据请求连接找到执行请求的controller类;而gateway采用同样的方式,用handlerMapping 堆请求连接进行解析匹配对应的Route,然后有对应的filter做对应的请求转发。整个流程用户请求先通过dispatchHandler找到对应的GateWayHandlerMapping ,再由GateWayHandlerMapping解析匹配对应的handler;handler处理完成后再经过filter链处理最终分发到proxied service。
请求先有DispatcherHandler进行处理,DispatchHandler在IOC容器初始化时会在容器中所有实现HandlerMapping接口的实例放到handlerMappers属性中,DispatchHandler调用handler方法迭代handlerMappings中的handlerMapping。RoutePredicateHandlerMapping是其中一个实现了HandlerMapping接口的类,它负责处理基于路由的处理的mapping实现类;RoutePredicateHandlerMapping 根据请求URL找到对应的路由配置信息将route的配置信息存放到ServerWebExchange中然后返回实现了WebHandler接口的FilterWebHandler,FilterWebHandler是一个存放过滤器的handler。最后DispatchHandler通过SimpleHandlerAdaptor适配器调用FilteringWebHandler的handle方法,FilteringWebHandler调用所有的过滤器,包括proxy filter,通过proxy filter请求被代理的服务;处理完毕后将response返回去。
handler 继承关系图
HandlerMapping和Ordered接口主要定义了获取getHandler和当前handler的加载顺序。AbstractHandlerMapping 在getHandler封装了CORS处理,因为所有的handler都会涉及到CORS处理,抽象到AbstractHandlerMapping处理,再提供getHandlerInternal让子类实现具体的查找方法在这里插入代码片。
/** *获取handler方法 **/ public Mono<Object> getHandler(ServerWebExchange exchange) { return this.getHandlerInternal(exchange).map((handler) -> { if (CorsUtils.isCorsRequest(exchange.getRequest())) { CorsConfiguration configA = this.globalCorsConfigSource.getCorsConfiguration(exchange); CorsConfiguration configB = this.getCorsConfiguration(handler, exchange); CorsConfiguration config = configA != null ? configA.combine(configB) : configB; if (!this.getCorsProcessor().process(config, exchange) || CorsUtils.isPreFlightRequest(exchange.getRequest())) { return REQUEST_HANDLED_HANDLER; } } return handler; }); } //抽象方法,让子类实现具体查找handler的 protected abstract Mono<?> getHandlerInternal(ServerWebExchange var1);我们可以通过代码学习学习到如果所有的接口的实现类都需要在接口定义方法或者后执行一些共性操作,我们可以通过抽象类实现共性的操作,再通过定义抽象方法,让子类实现特有的实现。这种模式叫做模板模式。
RoutePredicateHandlerMapping是处理获取handler。RoutePredicateHandlerMapping中的RouteLocator存储gateway启动时加载的路由对象信息。获取路由的时候,调用RoutePredicateHandlerMapping的getHandlerInternal方法,从routeLocator获取路由存放在WebServerExchange中,返回Filter。
private final RouteLocator routeLocator; @override public Mono<?> getHandlerExternal(ServerWebExchange exchange){ exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getClass().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()); }*/RoutePredicateHandlerMapping在创建的时候制定了WebHandler和RouteLocator实例。webhandler 封装了globalfilter而routelocator保存了所有的route对象。
routelocator主要作用是提供获取路由的类型。分析RoutePredicateHandlerMapping的时候,制动RoutePredicateHandlerMapping由routeLocator提供;下面分析一下RouteLocator加载路由。
最总的RouteLocator是CachingRoutingLocator ,加载过程是自上而下进行创建的。
Filter分为全局Filter和RouteFilter
Global Filter是上图列的filter,这些filter是通过GateWayAutoConfiguration中通过@Bean的方式进行创建。Router Filter 是在创建Route时指定的,属于自定义路由。在转发过程中,最终由Proxy Filter进行请求Proxy Service的,而这个proxy filter就是nettyroutefilter。通过下面代码我们可以看到在 **proxyRequest.setHeaders().send(request.getBody().map(dataBuffer->((NettyBuffer)dataBuffer).getNativeBuffers()));**中请求Proxy Service。
@Override 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.getMethod().toString()); final String url = requestUrl.toString(); HttpHeaders filtered = filterRequest(this.headersFilters.getIfAvailable(), exchange); final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders(); filtered.forEach(httpHeaders::set); String transferEncoding = request.getHeaders().getFirst(HttpHeaders.TRANSFER_ENCODING); boolean chunkedTransfer = "chunked".equalsIgnoreCase(transferEncoding); boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false); Mono<HttpClientResponse> responseMono = this.httpClient.request(method, url, req -> { final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach) .headers(httpHeaders) .chunkedTransfer(chunkedTransfer) .failOnServerError(false) .failOnClientError(false); if (preserveHost) { String host = request.getHeaders().getFirst(HttpHeaders.HOST); proxyRequest.header(HttpHeaders.HOST, host); } return proxyRequest.sendHeaders() //I shouldn't need this .send(request.getBody().map(dataBuffer -> ((NettyDataBuffer) dataBuffer).getNativeBuffer())); }); if (properties.getResponseTimeout() != null) { responseMono.timeout(properties.getResponseTimeout(), Mono.error(new TimeoutException("Response took longer than timeout: " + properties.getResponseTimeout()))); } return responseMono.doOnNext(res -> { 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())); if (headers.getContentType() != null) { exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, headers.getContentType()); } HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter( this.headersFilters.getIfAvailable(), headers, exchange, Type.RESPONSE); response.getHeaders().putAll(filteredResponseHeaders); HttpStatus status = HttpStatus.resolve(res.status().code()); if (status != null) { response.setStatusCode(status); } else if (response instanceof AbstractServerHttpResponse) { // https://jira.spring.io/browse/SPR-16748 ((AbstractServerHttpResponse) response).setStatusCodeValue(res.status().code()); } else { throw new IllegalStateException("Unable to set status code on response: " +res.status().code()+", "+response.getClass()); } // 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); }).then(chain.filter(exchange)); }