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比较牛 有点方 所以看起来 在思路上可能会有点费劲 就说这么多 这块最亮眼的 其实就是把配置转成了函数 个人觉得 这个可以在之后的编程中借鉴

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
dynamic-datasource-spring-boot-starter 是一个基于 Spring Boot 的动态数据源库。它提供了在运行时动态切换数据源、动态创建数据源以及多租户的支持。 对于 dynamic-datasource-spring-boot-starter 的源码分析,可以从以下几个方面进行思考: 1. 数据源的自动装配:源码中可能会使用 Spring Boot 的自动配置功能,通过扫描配置类或者注解的方式,自动将数据源相关的类和 bean 注入到应用程序中。 2. 动态切换数据源的实现:动态切换数据源是 dynamic-datasource-spring-boot-starter 的核心功能之一。源码中可能会涉及到 AOP、动态代理等技术,通过拦截器或者代理的方式,在运行时根据特定条件切换数据源。 3. 动态创建数据源的实现:动态-datasource-spring-boot-starter 提供了在运行时动态创建数据源的功能。源码中可能会包含一些工厂类或者构建者模式的实现,根据配置信息动态创建数据源实例。 4. 多租户支持的实现:多租户是指一个系统可以同时服务于多个不同的客户或租户。dynamic-datasource-spring-boot-starter 也提供了对多租户的支持。源码中可能会包含一些多租户相关的类和逻辑,如解析请求或者从配置文件中获取租户信息等。 总结来说,dynamic-datasource-spring-boot-starter 源码分析涉及到数据源的自动装配、动态切换数据源的实现、动态创建数据源和多租户支持等方面的内容。可以通过阅读源码来深入了解其实现原理和技术细节,从而更好地使用和定制该库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值