GateWay路由源码分析

GateWay路由源码分析

springgateway 简介

springcloud 提供了便于我们编写网关组件,分别是zuul和gateway;在zuul1的通信模型是BIO而zuul2的通信模型采用了NIO;spring gateway的IO模型使用的是NIO;但是从netflix发布zuul2后spring已经 开始 不集成zuul组件了。spring gateway的架构是基于WebFlux基础开发的。它依赖springboot2.x,spring5和netty

Spring GateWay请求转发流程

在spring MVC是通过handlermapping解析请求连接然后根据请求连接找到执行请求的controller类;而gateway采用同样的方式,用handlerMapping 堆请求连接进行解析匹配对应的Route,然后有对应的filter做对应的请求转发。整个流程用户请求先通过dispatchHandler找到对应的GateWayHandlerMapping ,再由GateWayHandlerMapping解析匹配对应的handler;handler处理完成后再经过filter链处理最终分发到proxied service。
GateWay路由源码分析_第1张图片

转发请求解析

GateWay路由源码分析_第2张图片
GateWay路由源码分析_第3张图片

  1. 请求先有DispatcherHandler进行处理,DispatchHandler在IOC容器初始化时会在容器中所有实现HandlerMapping接口的实例放到handlerMappers属性中,DispatchHandler调用handler方法迭代handlerMappings中的handlerMapping。
  2. RoutePredicateHandlerMapping是其中一个实现了HandlerMapping接口的类,它负责处理基于路由的处理的mapping实现类;RoutePredicateHandlerMapping 根据请求URL找到对应的路由配置信息将route的配置信息存放到ServerWebExchange中然后返回实现了WebHandler接口的FilterWebHandler,FilterWebHandler是一个存放过滤器的handler。
  3. 最后DispatchHandler通过SimpleHandlerAdaptor适配器调用FilteringWebHandler的handle方法,FilteringWebHandler调用所有的过滤器,包括proxy filter,通过proxy filter请求被代理的服务;处理完毕后将response返回去。

HandlerMapping,RouteLocator,GlobalFilter,WebHandler

HandlerMapping

handler 继承关系图
GateWay路由源码分析_第4张图片

AbstractHandlerMapping

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

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.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

routelocator主要作用是提供获取路由的类型。分析RoutePredicateHandlerMapping的时候,制动RoutePredicateHandlerMapping由routeLocator提供;下面分析一下RouteLocator加载路由。

Route组成

  • Proxy 代理的信息包括被代理的uri
  • predicate 包含请求的方法、path等信息
  • Filter Route自定义的过滤器

RouteLocator加载过程

GateWay路由源码分析_第5张图片
最总的RouteLocator是CachingRoutingLocator ,加载过程是自上而下进行创建的。

Route加载来源
  1. RouteLocatorBuilder ,在创建RouteLocatorBuilder的方法通过注入RouteLocatorBuilder来创建路由
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
  RouteLocator locator=builder.routes()
                               .route("get",r->r.path("security/**").uri("lb://security-server"))
                               .builder();
}
  1. Properties文件进行创建。properties路由创建包括PropertiesRouteDefinitionLocator和DiscoveryClientRouteDefinitionLocator两种方式。
  • PropertiesRouteDefinitionLocator通过配置文件创建
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://example.org
        predicates:
        - Cookie=chocolate, ch.p
  • DiscoveryClientRouteDefinitionLocator通过配置文件中lb://security-server指定服务名称,在eurek查找Service中的URI进行创建route。
  1. 通过MySQL或Redis、内存(InMemoryRouteDefinitionRepository)方式创建路由。实现RouteDefinitionRepository接口。InMemoryRouteDefinitionReposity为默认方式。
public class InMomeryRouteDefinitionRepository implements RouteDefintionRepository{
  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());
    }
}

GlobalFilter

GateWay路由源码分析_第6张图片

全局Filter加载过程

Filter分为全局Filter和RouteFilter

  • Global Filter是上图列的filter,这些filter是通过GateWayAutoConfiguration中通过@Bean的方式进行创建。
  • Router Filter 是在创建Route时指定的,属于自定义路由。
NettyRoutingFilter

在转发过程中,最终由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));
    }

SpringGateWay 思维导图

GateWay路由源码分析_第7张图片

你可能感兴趣的:(springcloud)