spring-gateway请求流程 源码分析

DispatcherHandler

public Mono<Void> handle(ServerWebExchange exchange) {
		if (logger.isDebugEnabled()) {
			ServerHttpRequest request = exchange.getRequest();
			logger.debug("Processing " + request.getMethodValue() + " request for [" + request.getURI() + "]");
		}
		if (this.handlerMappings == null) {
			return Mono.error(HANDLER_NOT_FOUND_EXCEPTION);
		}
		return Flux.fromIterable(this.handlerMappings)
				.concatMap(mapping -> mapping.getHandler(exchange))
				.next()
				.switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION))
				.flatMap(handler -> invokeHandler(exchange, handler))
				.flatMap(result -> handleResult(exchange, result));
	}

首先 DispatcherHandler.handle() 是所有请求的入口(我一开始 就猜是这个)
整个请求 在该方法内进行流转 完成请求的 路由解析 转发 响应 数据返回

  • 路由解析
Flux.fromIterable(this.handlerMappings).concatMap(mapping -> mapping.getHandler(exchange))

这段代码 根据请求的信息 进行路由解析 其中

this.handlerMappings----->@Nullable  private List<HandlerMapping> handlerMappings;

加载HandlerMapping HandlerAdapter HandlerResultHandler的实现类
这些东西是容器写好的 是webflux的基本东西
protected void initStrategies(ApplicationContext context) {
		Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
				context, HandlerMapping.class, true, false);

		ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
		AnnotationAwareOrderComparator.sort(mappings);
		this.handlerMappings = Collections.unmodifiableList(mappings);

		Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
				context, HandlerAdapter.class, true, false);

		this.handlerAdapters = new ArrayList<>(adapterBeans.values());
		AnnotationAwareOrderComparator.sort(this.handlerAdapters);

		Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
				context, HandlerResultHandler.class, true, false);

		this.resultHandlers = new ArrayList<>(beans.values());
		AnnotationAwareOrderComparator.sort(this.resultHandlers);
	}

会从handlerMappings的list中 对HandlerMapping的实现类(这个spring容器自己加进来的)
然后 这里面的getHander()方法 调用的其实是 AbstractHandlerMapping.getHandler() 方法

@Override
	public Mono<Object> getHandler(ServerWebExchange exchange) {
		return getHandlerInternal(exchange).map(handler -> {
			if (CorsUtils.isCorsRequest(exchange.getRequest())) {
				CorsConfiguration configA = this.globalCorsConfigSource.getCorsConfiguration(exchange);
				CorsConfiguration configB = getCorsConfiguration(handler, exchange);
				CorsConfiguration config = (configA != null ? configA.combine(configB) : configB);
				if (!getCorsProcessor().process(config, exchange) ||
						CorsUtils.isPreFlightRequest(exchange.getRequest())) {
					return REQUEST_HANDLED_HANDLER;
				}
			}
			return handler;
		});
	}

这里面有一个getHandlerInternal() 方法 这个方法在RoutePredicateHandlerMapping以及 RouterFunctionMapping 中有实现
所以调用的是他们的这个方法

  • RoutePredicateHandlerMapping.getHandlerInternal()

	@Override
	protected Mono<?> getHandlerInternal(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) + "]");
					}
				})));
	}
  • RouterFunctionMapping.getHandlerInternal()
@Override
	protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
		if (this.routerFunction != null) {
			ServerRequest request = ServerRequest.create(exchange, this.messageReaders);
			exchange.getAttributes().put(RouterFunctions.REQUEST_ATTRIBUTE, request);
			return this.routerFunction.route(request);
		}
		else {
			return Mono.empty();
		}
	}

最后根据我们的配置文件 其实真正调用的是 RoutePredicateHandlerMapping.getHandlerInternal() ,貌似下面这个想要用的话 需要在代码里面配置 路由信息 还没试过
好了 到目前为止 我们还没有进行路由的寻找 这个时候 其实路由的信息还未生成 只有一堆的数据信息而已

[路由生成]

lookupRoute(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());
			}*/
	}

这里 通过this.routeLocator.getRoutes() 返回路由的配置信息
在调用 java的函数Predicate(这个是一个java内的基础函数 返回bool)来判断当前请求是否符合路由配置文件中的 predicates
这里特别说明一下 我们所配置的 predicates: filters:最后都被转为了java的lambel函数 用来与当前请求进行匹配 从而返回匹配成功的
至于这个函数式怎么转的 也不复杂 不过要放到后面说了 先把主线的说完

 gateway:
          routes:
          - id: rewritepath_route
            uri: lb://has-zk-app
            predicates:
            - Path=/foo/**
            filters:
            - RewritePath=/foo/(?<segment>.*), /$\{segment}

这个this.routeLocator.getRoutes() 最后实际调用的是
RouteDefinitionRouteLocator.getRoutes()

  • RouteDefinitionRouteLocator.getRoutes()
@Override
	public Flux<Route> getRoutes() {
		return this.routeDefinitionLocator.getRouteDefinitions()
				.map(this::convertToRoute)
				//TODO: error handling
				.map(route -> {
					if (logger.isDebugEnabled()) {
						logger.debug("RouteDefinition matched: " + route.getId());
					}
					return route;
				});


		/* TODO: trace logging
			if (logger.isTraceEnabled()) {
				logger.trace("RouteDefinition did not match: " + routeDefinition.getId());
			}*/
	}

其中 this::convertToRoute 这个方法 才真正的将我们的配置文件 信息转换成真正的路由信息 我们所配置的 predicates: filters:转为函数
这两个 配置predicates 表示 我们发过来请求 是否可以被 该路由处理 第二个表示该请求被如何处理 另外 Path RewritePath 这些不是自定义的 能配的 就那么几个 可以到官网查

  • RouteDefinitionRouteLocator.convertToRoute
private Route convertToRoute(RouteDefinition routeDefinition) {
		AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
		List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);

		return Route.async(routeDefinition)
				.asyncPredicate(predicate)
				.replaceFilters(gatewayFilters)
				.build();
	}
	

combinePredicates(routeDefinition); 这个方法一眼就明白 把配置转换成函数
List gatewayFilters = getFilters(routeDefinition); 这个把配置转 过滤器 最后在返回 一个Route

到这里为止 route就弄好了 之后就匹配 转发 在把结果返回给客户端 分别对应
.flatMap(handler -> invokeHandler(exchange, handler))
.flatMap(result -> handleResult(exchange, result));
这里还会将路由信息进行缓存 不然每次进来都要这么跑一遍

总结

这里使用的架构其实还是springmvc那一套 mapping fliter hander 如果熟悉那一套规则 这块看起来应该不费劲(很遗憾 我么看过那套的源码 只知道架构 我找入口找了半天) 我们可以将这个配置文件 与我们配置的cotroller进行类比 cotroller需要一个匹配的请求路径 这个需要一个 predicte配置 都差不多 就是里面的细节 比较麻烦 特别是gateway使用了webflux(可以先去补补课 看看这个)里面的一些api比较牛 有点方 所以看起来 在思路上可能会有点费劲 就说这么多 这块最亮眼的 其实就是把配置转成了函数 个人觉得 这个可以在之后的编程中借鉴

    原文作者:Spring MVC
    原文地址: https://blog.csdn.net/zhaoyu_nb/article/details/84984042
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞