【阿里技术文章学习】Spring Cloud Gateway一次请求调用源码解析

原文链接:Spring Cloud Gateway一次请求调用源码解析

基础知识:

在使用Spring Cloud Gateway的时候需要理解三个模块,即

Route:

即一套路由规则,是集URI、predicate、filter等属性的一个元数据类。

Predicate:

这是Java8函数式编程的一个方法,这里可以看做是满足什么条件的时候,route规则进行生效。

Filter:

filter可以认为是Spring Cloud Gateway最核心的模块,熔断、安全、逻辑执行、网络调用都是filter来完成的,其中又细分为gateway filter和global filter,区别在于是具体一个route规则生效还是所有route规则都生效。

以下代码实例

 @RequestMapping("/paramTest")
  public Object paramTest(@RequestParam Map<String,Object> param) {
      return param.get("name");
  }

  @Bean
  public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
	return builder.routes()
      .route("path_route", r ->
                   r.path("/get")
                  .filters(f -> f.addRequestParameter("name", "value"))
                  .uri("forward:///paramTest"))
      .build();
  }

.route方法代表的就是一个路由规则;

.path方法代表的就是一个predicate,背后的现实是PathRoutePredicateFactory,在这段代码的含义即当路径包含/get的时候,当前规则生效。

.filters方法的意思即给当前路由规则添加一个增加请求参数的filter,每次请求都对参数里添加 name:value 的键值对;

.uri 方法的含义即最终路由到哪里去,这里的forward前缀会将请求交给spring mvc的DispatcherHandler进行路由,进行本机的逻辑调用,除了forward以外还可以使用http、https前缀进行http调用,lb前缀可以在配置注册中心后进行rpc调用。(常用lb,需要在服务中配置后,注册到注册中心,然后可以通过网关路由到对应的服务)

下图是Spring Cloud Gateway官方文档给出的一个工作原理图,Spring Cloud Gateway 接收到请求后进行路由规则的匹配,然后交给web handler 进行处理,web handler 会执行一系列的filter逻辑。

流程分析:

  1. 接受请求
    Spring Cloud Gateway的底层框架是netty,接受请求的关键类是ReactorHttpHandlerAdapter,做的事情很简单,就是将netty的请求、响应转为http的请求、响应并交给一个http handler执行后面的逻辑
    以下为源码:
	public Mono<Void> apply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) {
	    NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(reactorResponse.alloc());
	
	     try {
	     	//转换请求
	         ReactorServerHttpRequest request = new ReactorServerHttpRequest(reactorRequest, bufferFactory);
	         ServerHttpResponse response = new ReactorServerHttpResponse(reactorResponse, bufferFactory);
	         if (request.getMethod() == HttpMethod.HEAD) {
	             response = new HttpHeadResponseDecorator((ServerHttpResponse)response);
	         }
	
	         return this.httpHandler.handle(request, (ServerHttpResponse)response).doOnError((ex) -> {
	             logger.trace(request.getLogPrefix() + "Failed to complete: " + ex.getMessage());
	         }).doOnSuccess((aVoid) -> {
	             logger.trace(request.getLogPrefix() + "Handling completed");
	         });
	     } catch (URISyntaxException var6) {
	         if (logger.isDebugEnabled()) {
	             logger.debug("Failed to get request URI: " + var6.getMessage());
	         }
	
	         reactorResponse.status(HttpResponseStatus.BAD_REQUEST);
	         return Mono.empty();
	     }
	}
  1. WEB过滤器链
    http handler做的事情第一是将request 和 response转为一个exchange,这个exchange非常核心,是各个filter之间参数流转的载体,该类包含request、response、attributes(扩展字段),接着做的事情就是web filter链的执行,其中的逻辑主要是监控。
    图片来源于阿里技术
    其中WebfilterChainParoxy 又会引出新的一条filter链,主要是安全、日志、认证相关的逻辑,由此可见Spring Cloud Gateway的过滤器设计是层层嵌套,扩展性很强。(网关大部分核心处理逻辑可以在此filter链实现)
    图片来源于阿里技术

  2. 寻找路由规则
    核心类是RoutePredicateHandlerMapping,逻辑也非常简单,就是把所有的route规则的predicate遍历一遍看哪个predicate能够命中,核心代码是:

        protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
            return this.routeLocator.getRoutes().concatMap((route) -> {
                return Mono.just(route).filterWhen((r) -> {
                    exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
                    return (Publisher)r.getPredicate().apply(exchange);
                }).doOnError((e) -> {
                    this.logger.error("Error applying predicate for route: " + route.getId(), e);
                }).onErrorResume((e) -> {
                    return Mono.empty();
                });
            }).next().map((route) -> {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Route matched: " + route.getId());
                }
    
                this.validateRoute(route, exchange);
                return route;
            });
        }
    

    PathRoutePredicateFactory路由规则

    public Predicate<ServerWebExchange> apply(Config config) {
    		final ArrayList<PathPattern> pathPatterns = new ArrayList<>();
    		synchronized (this.pathPatternParser) {
    			pathPatternParser.setMatchOptionalTrailingSeparator(
    					config.isMatchOptionalTrailingSeparator());
    			config.getPatterns().forEach(pattern -> {
    				PathPattern pathPattern = this.pathPatternParser.parse(pattern);
    				pathPatterns.add(pathPattern);
    			});
    		}
    		return new GatewayPredicate() {
    			@Override
    			public boolean test(ServerWebExchange exchange) {
    				PathContainer path = parsePath(
    						exchange.getRequest().getURI().getRawPath());
    
    				PathPattern match = null;
    				for (int i = 0; i < pathPatterns.size(); i++) {
    					PathPattern pathPattern = pathPatterns.get(i);
    					if (pathPattern.matches(path)) {
    						match = pathPattern;
    						break;
    					}
    				}
    
    				if (match != null) {
    					traceMatch("Pattern", match.getPatternString(), path, true);
    					PathMatchInfo pathMatchInfo = match.matchAndExtract(path);
    					putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
    					return true;
    				}
    				else {
    					traceMatch("Pattern", config.getPatterns(), path, false);
    					return false;
    				}
    			}
    
    			@Override
    			public String toString() {
    				return String.format("Paths: %s, match trailing slash: %b",
    						config.getPatterns(), config.isMatchOptionalTrailingSeparator());
    			}
    		};
    	}
    

    因为我这里用的是path进行过滤,所以背后的逻辑是PathRoutePredicateFactory来完成的,除了PathRoutePredicateFactory还有很多predicate规则。
    图片来源于阿里技术

  3. 核心过滤器链执行

    找到路由规则后下一步就是执行了,这里的核心类是FilteringWebHandler,其中的源码为

    	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);
    
    		if (logger.isDebugEnabled()) {
    			logger.debug("Sorted gatewayFilterFactories: " + combined);
    		}
    
    		return new DefaultGatewayFilterChain(combined).filter(exchange);
    	}
    

    做的事情很简单:

    1. 获取route级别的过滤器
    2. 获取全局过滤器
    3. 两种过滤器放在一起并根据order进行排序
    4. 执行过滤器链
      图片来源于阿里技术
      因为我的配置里包含了一个添加请求参数的逻辑,所以红线箭头处就是我配置的gateway filter名为 AddRequestParameterGatewayFilterFactory,其余全是Gloabl Filter,这些过滤器的功能主要是url解析,请求转发,响应回写等逻辑,因为我们这里用的是forward schema,所以请求转发会由ForwardRoutingFilter进行执行。
  4. 请求转发

    ForwardRoutingFilter做的事情也很简单,直接复用了spring mvc的能力,将请求提交给dispatcherHandler进行处理,dispatcherHandler会根据path前缀找到需要目标处理器执行逻辑。

    	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    		URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
    
    		String scheme = requestUrl.getScheme();
    		if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) {
    			return chain.filter(exchange);
    		}
    
    		// TODO: translate url?
    
    		if (log.isTraceEnabled()) {
    			log.trace("Forwarding to URI: " + requestUrl);
    		}
    
    		return this.getDispatcherHandler().handle(exchange);
    	}
    
  5. 响应回写
    响应回写的核心类是NettyWriteResponseFilter,但是大家可以注意到执行器链中NettyWriteResponseFilter的排序是在最前面的,按道理这种响应处理的类应该是在靠后才对,这里的设计比较巧妙。大家可以看到chain.filter(exchange).then(),意思就是执行到我的时候直接跳过下一个,等后面的过滤器都执行完后才执行这段逻辑,这种行为控制的方法值得学习。

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    		// NOTICE: nothing in "pre" filter stage as CLIENT_RESPONSE_CONN_ATTR is not added
    		// until the NettyRoutingFilter is run
    		// @formatter:off
    		return chain.filter(exchange)
    				.doOnError(throwable -> cleanup(exchange))
    				.then(Mono.defer(() -> {
    					Connection connection = exchange.getAttribute(CLIENT_RESPONSE_CONN_ATTR);
    
    					if (connection == null) {
    						return Mono.empty();
    					}
    					if (log.isTraceEnabled()) {
    						log.trace("NettyWriteResponseFilter start inbound: "
    								+ connection.channel().id().asShortText() + ", outbound: "
    								+ exchange.getLogPrefix());
    					}
    					ServerHttpResponse response = exchange.getResponse();
    
    					// TODO: needed?
    					final Flux<DataBuffer> body = connection
    							.inbound()
    							.receive()
    							.retain()
    							.map(byteBuf -> wrap(byteBuf, response));
    
    					MediaType contentType = null;
    					try {
    						contentType = response.getHeaders().getContentType();
    					}
    					catch (Exception e) {
    						if (log.isTraceEnabled()) {
    							log.trace("invalid media type", e);
    						}
    					}
    					return (isStreamingMediaType(contentType)
    							? response.writeAndFlushWith(body.map(Flux::just))
    							: response.writeWith(body));
    				})).doOnCancel(() -> cleanup(exchange));
    		// @formatter:on
    	}
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值