Spring Cloud Gateway 源码

embedded/2024/12/28 2:24:11/

Spring Cloud Gateway 架构图
在这里插入图片描述
按照以上架构图,请求的处理流程:
1.客户端请求发送到网关 DispatcherHandler
2.网关通过 HandlerMapping 找到相应的 WebHandler
3.WebHandler生成FilterChain过滤器链执行所有的过滤器
4.返回Response结果

自动装配类GatewayAutoConfiguration
在这里插入图片描述
看几个关键的Bean
路由定义加载

java">PropertiesRouteDefinitionLocator  
CompositeRouteDefinitionLocator
RouteDefinitionRouteLocator

HandlerMapping类

java">RoutePredicateHandlerMapping

WebHandler类 核心逻辑

java">FilteringWebHandler

RoutePredicateFactory 路由断言工厂,很多只列举部分

java">WeightCalculatorWebFilter // 权重
BeforeRoutePredicateFactory // 时间
HeaderRoutePredicateFactory // 请求头匹配
HostRoutePredicateFactory // 地址匹配
MethodRoutePredicateFactory // 请求方法
CookieRoutePredicateFactory // cookie匹配
......

GatewayFilterFactory 网关过滤器工厂,很多只列举部分

java">AddRequestHeaderGatewayFilterFactory // 添加请求头过滤器
MapRequestHeaderGatewayFilterFactory // 请求头值替换过滤器
AddRequestParameterGatewayFilterFactory // 添加请求参数过滤器
AddResponseHeaderGatewayFilterFactory // 添加响应头过滤器
ModifyRequestBodyGatewayFilterFactory // 修改请求实体过滤器
......

核心入口
DispatcherHandler.handle(ServerWebExchange exchange) 方法

java">public Mono<Void> handle(ServerWebExchange exchange) {if (this.handlerMappings == null) {return createNotFoundError();}if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {return handlePreFlight(exchange);}return Flux.fromIterable(this.handlerMappings)// 获取handler.concatMap(mapping -> mapping.getHandler(exchange)).next().switchIfEmpty(createNotFoundError())// 通过handlerAdapter执行handler.flatMap(handler -> invokeHandler(exchange, handler))// 处理结果.flatMap(result -> handleResult(exchange, result));
}

获取处理器 mapping.getHandler(exchange)
通过RoutePredicateHandlerMapping 找到 FilteringWebHandler
在这里插入图片描述
最终获取到的handler是 private final FilteringWebHandler webHandler;

java">protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {// don't handle requests on management port if set and different than server portif (this.managementPortType == DIFFERENT && this.managementPort != null&& exchange.getRequest().getURI().getPort() == this.managementPort) {return Mono.empty();}exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, 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) + "]");}})));
}

执行处理器 invokeHandler(exchange, handler)

java">private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {if (ObjectUtils.nullSafeEquals(exchange.getResponse().getStatusCode(), HttpStatus.FORBIDDEN)) {return Mono.empty();  // CORS rejection}if (this.handlerAdapters != null) {for (HandlerAdapter handlerAdapter : this.handlerAdapters) {if (handlerAdapter.supports(handler)) {return handlerAdapter.handle(exchange, handler);}}}return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}

最终是由SimpleHandlerAdapter执行handler
在这里插入图片描述
具体的处理逻辑 webHandler.handle(exchange)

java">public class SimpleHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return WebHandler.class.isAssignableFrom(handler.getClass());}@Overridepublic Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {WebHandler webHandler = (WebHandler) handler;Mono<Void> mono = webHandler.handle(exchange);return mono.then(Mono.empty());}}

最终进入FilteringWebHandler.handle(ServerWebExchange exchange)方法

java">public Mono<Void> handle(ServerWebExchange exchange) {Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);// 获取当前路配置的GatewayFilterList<GatewayFilter> gatewayFilters = route.getFilters();// 加上所有全局过滤器List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);combined.addAll(gatewayFilters);// 按Ordered排序AnnotationAwareOrderComparator.sort(combined);if (logger.isDebugEnabled()) {logger.debug("Sorted gatewayFilterFactories: " + combined);}// 过滤器链执行return new DefaultGatewayFilterChain(combined).filter(exchange);
}

每个过滤器执行完,重新封装新的过滤器链,过滤器链索引+1继续执行

java">public Mono<Void> filter(ServerWebExchange exchange) {return Mono.defer(() -> {if (this.index < filters.size()) {GatewayFilter filter = filters.get(this.index);DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);return filter.filter(exchange, chain);}else {return Mono.empty(); // complete}});
}

过滤器链执行
网关会在各种过滤器处理后(根据配置的过滤器修改请求信息,重写路径等),最后转发到对应服务,核心就是通过NettyRoutingFilter来实现的
在这里插入图片描述
NettyRoutingFilter会代理发送http请求到对应的服务器,最终返回结果

java">public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);String scheme = requestUrl.getScheme();// 判断是否已路由,是否http请求if (isAlreadyRouted(exchange) || (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme))) {return chain.filter(exchange);}setAlreadyRouted(exchange);ServerHttpRequest request = exchange.getRequest();final HttpMethod method = HttpMethod.valueOf(request.getMethodValue());final String url = requestUrl.toASCIIString();HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();filtered.forEach(httpHeaders::set);boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);// 构建请求并发送,最终返回结果Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange).headers(headers -> {headers.add(httpHeaders);// Will either be set below, or later by Nettyheaders.remove(HttpHeaders.HOST);if (preserveHost) {String host = request.getHeaders().getFirst(HttpHeaders.HOST);headers.add(HttpHeaders.HOST, host);}}).request(method).uri(url).send((req, nettyOutbound) -> {if (log.isTraceEnabled()) {nettyOutbound.withConnection(connection -> log.trace("outbound route: "+ connection.channel().id().asShortText() + ", inbound: " + exchange.getLogPrefix()));}return nettyOutbound.send(request.getBody().map(this::getByteBuf));}).responseConnection((res, connection) -> {// Defer committing the response until all route filters have run// Put client response as ServerWebExchange attribute and write// response later NettyWriteResponseFilterexchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);ServerHttpResponse response = exchange.getResponse();// put headers and status so filters can modify the responseHttpHeaders headers = new HttpHeaders();res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);if (StringUtils.hasLength(contentTypeValue)) {exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, contentTypeValue);}setResponseStatus(res, response);// make sure headers filters run after setting status so it is// available in responseHttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(getHeadersFilters(), headers, exchange,Type.RESPONSE);if (!filteredResponseHeaders.containsKey(HttpHeaders.TRANSFER_ENCODING)&& filteredResponseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH)) {// It is not valid to have both the transfer-encoding header and// the content-length header.// Remove the transfer-encoding header in the response if the// content-length header is present.response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);}exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet());response.getHeaders().addAll(filteredResponseHeaders);return Mono.just(res);});Duration responseTimeout = getResponseTimeout(route);if (responseTimeout != null) {responseFlux = responseFlux.timeout(responseTimeout,Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout))).onErrorMap(TimeoutException.class,th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th));}return responseFlux.then(chain.filter(exchange));
}

最后一个ForwardRoutingFilter判断是否需要转发到对应地址的

java">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);
}

到此Spring Cloud Gateway的核心处理逻辑就分析完了,主要是针对核心逻辑链路的处理,很多细节都没深入,有兴趣可以自行debug看看


http://www.ppmy.cn/embedded/148974.html

相关文章

【玩转OCR | 腾讯云智能结构化OCR在图像增强与发票识别中的应用实践】

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 引言 图像增强API调用实践1. API选择与参数设置2. 在线调试与结果分析3. 响应结果具体实现代码 发票…

阿里云DataWorks产品使用

大家好&#xff0c;我是 V 哥。DataWorks 是阿里云提供的大数据开发治理平台&#xff0c;它集成了多种大数据引擎&#xff0c;提供了从数据采集、存储、开发、治理到分析和可视化的全生命周期解决方案。以下是对 DataWorks 的详细评测&#xff1a; 任务开发便捷性&#xff1a; …

【C语言】成绩等级制

将成绩分为A、B、C、D、E等级。具体的等级划分如下&#xff1a; A&#xff1a;90分及以上B&#xff1a;80分到89分C&#xff1a;70分到79分D&#xff1a;60分到69分E&#xff1a;60分以下 #include <stdio.h> int main() {float score 0;printf("请输入学生成绩&a…

Java的list中状态属性相同返回true的实现方案

文章目录 项目背景方案一、for循环实现实现思路 方案二、stream实现实现思路 项目背景 在项目中会遇到list中多个状态判断&#xff0c;状态值相等时&#xff0c;总体返回为true。 方案一、for循环实现 实现思路 遍历list&#xff0c;当出现不一致时&#xff0c;直接跳出循环…

VR 动感单车身心调适系统的功能与作用

如今&#xff0c;人们面临着来自各方的压力&#xff0c;国家重视国民身心健康&#xff0c;但人们在实际生活中却缺乏有效的身心调节方式。无论是久坐的白领&#xff0c;还是学业繁重的学生&#xff0c;都存在身体亚健康和心理压力大的问题。传统健身方式枯燥、心理咨询成本高且…

网络管理-期末项目(附源码)

环境&#xff1a;网络管理 主机资源监控系统项目搭建 &#xff08;保姆级教程 建议点赞 收藏&#xff09;_搭建网络版信息管理系统-CSDN博客 效果图 下面3个文件的项目目录(python3.8.8的虚拟环境) D:\py_siqintu\myproject5\Scripts\mytest.py D:\py_siqintu\myproject5\Sc…

智源研究院与安谋科技达成战略合作,共建开源AI“芯”生态

12月25日&#xff0c;智源研究院与安谋科技&#xff08;中国&#xff09;有限公司&#xff08;以下简称“安谋科技”&#xff09;与正式签署战略合作协议&#xff0c;双方将面向多元AI芯片领域开展算子库优化与适配、编译器与工具链支持、生态系统建设与推广等一系列深入合作&a…

table 表格转成 excell 导出

OK&#xff0c;功能非常简单&#xff0c;但是很实用啊&#xff01; 依赖安装 这里我们需要安装两个依赖&#xff1a; xlsx 和 file-saver&#xff0c;就可以帮助我们实现功能了&#xff01; npm i xlsx file-saver代码参考 导出方法 utils/index.js import * as XLSX from …