• SpringBoot Gateway源码讲解


    背景:

    在微服务架构中,通常一个系统会被拆分为多个微服务,面对这么多微服务客户端应该如何去调用呢?如果没有其他更优方法,我们只能记录每个微服务对应的地址,分别去调用,但是这样会有很多的问题和潜在因素。

    客户端多次请求不同的微服务,会增加客户端代码和配置的复杂性,维护成本比价高。

    认证复杂,每个微服务可能存在不同的认证方式,客户端去调用,要去适配不同的认证,

    存在跨域的请求,调用链有一定的相对复杂性(防火墙 / 浏览器不友好的协议)。

    难以重构,随着项目的迭代,可能需要重新划分微服务

    为了解决上面的问题,微服务引入了 网关 的概念,网关为微服务架构的系统提供简单、有效且统一的API路由管理,作为系统的统一入口,提供内部服务的路由中转,给客户端提供统一的服务,可以实现一些和业务没有耦合的公用逻辑,主要功能包含认证、鉴权、路由转发、安全策略、防刷、流量控制、监控日志等

    在这里插入图片描述

    网关对比

    Zuul 1.0 : Netflix开源的网关,使用Java开发,基于Servlet架构构建,便于二次开发。因为基于Servlet内部延迟严重,并发场景不友好,一个线程只能处理一次连接请求。

    Zuul 2.0 : 采用Netty实现异步非阻塞编程模型,一个CPU一个线程,能够处理所有的请求和响应,请求响应的生命周期通过事件和回调进行处理,减少线程数量,开销较小
    在这里插入图片描述
    GateWay : 是Spring Cloud的一个全新的API网关项目,替换Zuul开发的网关服务,基于Spring5.0 + SpringBoot2.0 + WebFlux(基于性能的Reactor模式响应式通信框架Netty,异步阻塞模型)等技术开发,性能高于Zuul

    Nginx+lua : 性能要比上面的强很多,使用Nginx的反向代码和负载均衡实现对API服务器的负载均衡以及高可用,lua作为一款脚本语言,可以编写一些简单的逻辑,但是无法嵌入到微服务架构中

    Kong : 基于OpenResty(Nginx + Lua模块)编写的高可用、易扩展的,性能高效且稳定,支持多个可用插件(限流、鉴权)等,开箱即可用,只支持HTTP协议,且二次开发扩展难,缺乏更易用的管理和配置方式

    GateWay
    官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-starter

    Spring Cloud Gateway 是Spring Cloud的一个全新的API网关项目,目的是为了替换掉Zuul1,它基于Spring5.0 + SpringBoot2.0 + WebFlux(基于性能的Reactor模式响应式通信框架Netty,异步阻塞模型)等技术开发,性能于Zuul,官测试,Spring Cloud GateWay是Zuul的1.6倍 ,旨在为微服务架构提供种简单有效的统的API路由管理式。

    可以与Spring Cloud Discovery Client(如Eureka)、Ribbon、Hystrix等组件配合使用,实现路由转发、负载均衡、熔断、鉴权、路径重写、志监控等

    Gateway还内置了限流过滤器,实现了限流的功能。

    设计优雅,容易拓展

    基本概念

    **路由(Route)**是GateWay中最基本的组件之一,表示一个具体的路由信息载体,主要由下面几个部分组成:

    id:路由唯一标识,区别于其他的route

    url: 路由指向的目的地URL,客户端请求最终被转发到的微服务

    order: 用于多个Route之间的排序,数值越小越靠前,匹配优先级越高

    predicate:断言的作用是进行条件判断,只有断言为true,才执行路由

    filter: 过滤器用于修改请求和响应信息

    核心流程

    在这里插入图片描述

    源码时序图

    在这里插入图片描述

    源码讲解

    请求进来先被HttpWebHandlerAdapter拦截到

    
    	public HttpWebHandlerAdapter(WebHandler delegate) {
    		//此处会执行 this.delegate = delegate;, delegate 为 DispatcherHandler
    		super(delegate);
    	}
    	
    	@Override
    	public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
    		if (this.forwardedHeaderTransformer != null) {
    			request = this.forwardedHeaderTransformer.apply(request);
    		}
    		//封装为DefaultServerWebExchange对象
    		ServerWebExchange exchange = createExchange(request, response);
    
    		LogFormatUtils.traceDebug(logger, traceOn ->
    				exchange.getLogPrefix() + formatRequest(exchange.getRequest()) +
    						(traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : ""));
    		//执行到此出,为reactor编程模式
    		return getDelegate().handle(exchange)
    				.doOnSuccess(aVoid -> logResponse(exchange))
    				.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
    				.then(Mono.defer(response::setComplete));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    我们看下reactor编程模式的这几个方法

    1、getDelegate()

    	public WebHandler getDelegate() {
    		//返回DispatcherHandler实例
    		return this.delegate;
    	}
    
    • 1
    • 2
    • 3
    • 4

    2、handle(exchange)

    	@Override
    	public Mono<Void> handle(ServerWebExchange exchange) {
    		if (this.handlerMappings == null) {
    			return createNotFoundError();
    		}
    		//执行该方法
    		//	Flux.fromIterable(this.handlerMappings) 将handlerMappings转化为 Flux
    		//1、concatMap中调用mapping.getHandler
    		//2、调用next获取匹配的一个Mono
    		//3、flatMap中调用invokeHandler方法
    		//4、flatMap调用handleResult方法
    		return Flux.fromIterable(this.handlerMappings)
    				.concatMap(mapping -> mapping.getHandler(exchange))
    				.next()
    				.switchIfEmpty(createNotFoundError())
    				.flatMap(handler -> invokeHandler(exchange, handler))
    				.flatMap(result -> handleResult(exchange, result));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.1、mapping.getHandler
    org.springframework.web.reactive.handler.AbstractHandlerMapping#getHandler

    	@Override
    	public Mono<Object> getHandler(ServerWebExchange exchange) {
    		//1、调用getHandlerInternal,从配置的多个Route中获取匹配的
    		return getHandlerInternal(exchange).map(handler -> {
    			if (logger.isDebugEnabled()) {
    				logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
    			}
    			ServerHttpRequest request = exchange.getRequest();
    			if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
    				CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null);
    				CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);
    				config = (config != null ? config.combine(handlerConfig) : handlerConfig);
    				if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
    					return REQUEST_HANDLED_HANDLER;
    				}
    			}
    			return handler;
    		});
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.1.1 getHandlerInternal
    org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping#getHandlerInternal

    	@Override
    	protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
    		// don't handle requests on management port if set and different than server port
    		if (this.managementPortType == DIFFERENT && this.managementPort != null
    				&& exchange.getRequest().getURI().getPort() == this.managementPort) {
    			return Mono.empty();
    		}
    		exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());
    		//1、调用lookupRoute获取合适的Route
    		//	统一返回FilteringWebHandler处理器
    		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);
    					//统一返回FilteringWebHandler处理器
    					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) + "]");
    					}
    				})));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2.1.1.1 lookupRoute
    org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping#lookupRoute

    	protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
    		//1、this.routeLocator.getRoutes()拿到定义的所有的route信息
    		//2、Mono.just(route).filterWhen(return r.getPredicate().apply(exchange)) 通过route的断言判断 route是否符合
    		//3、返回route
    		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;
    				});
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2.2、invokeHandler方法调用

    	private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
    		if (this.handlerAdapters != null) {
    			for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
    				//org.springframework.web.reactive.result.SimpleHandlerAdapter#supports
    				//判断是否WebHandler.class
    				if (handlerAdapter.supports(handler)) {
    					//org.springframework.web.reactive.result.SimpleHandlerAdapter#handle
    					//1、方法内部调用FilteringWebHandler的handle方法
    					return handlerAdapter.handle(exchange, handler);
    				}
    			}
    		}
    		return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2.1、handlerAdapter.handle(exchange, handler)

    	@Override
    	public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
    		WebHandler webHandler = (WebHandler) handler;
    		//调用FilteringWebHandler#handle方法
    		Mono<Void> mono = webHandler.handle(exchange);
    		//返回mono
    		return mono.then(Mono.empty());
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.2.1、FilteringWebHandler#handle
    org.springframework.cloud.gateway.handler.FilteringWebHandler#handle

    	@Override
    	public Mono<Void> handle(ServerWebExchange exchange) {
    		//从exchange获取到匹配上的route
    		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
    		//获取route定义的过滤器
    		List<GatewayFilter> gatewayFilters = route.getFilters();
    
    		List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
    		combined.addAll(gatewayFilters);
    		// 排序
    		AnnotationAwareOrderComparator.sort(combined);
    
    		if (logger.isDebugEnabled()) {
    			logger.debug("Sorted gatewayFilterFactories: " + combined);
    		}
    		//形成责任链模式 依次调用filter方法
    		return new DefaultGatewayFilterChain(combined).filter(exchange);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    参考文档SpringCloud GateWay 万字详解

  • 相关阅读:
    Spine Web Player教程
    LeetCode_数学分析_中等_754.到达终点数字
    tensorrt: pycuda, onnx, onnxruntime, tensorrt,torch-tensorrt 安装
    操作系统reboot之后ddr内容还在吗?
    AC修炼计划(AtCoder Beginner Contest 329)
    宝塔自建bitwarden密码管理器
    JAVA文件和多个参数前后台交互
    古记事法:Windows 下 16 位汇编环境搭建指南(DOSBox-X 篇)
    Python实现九九乘法表的几种方式,入门必备案例~超级简单~
    SLAM从入门到精通(编写第一个package)
  • 原文地址:https://blog.csdn.net/qq_36364521/article/details/125984350