Spring Cloud Gateway 源码

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

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

PropertiesRouteDefinitionLocator  
CompositeRouteDefinitionLocator
RouteDefinitionRouteLocator

HandlerMapping类

RoutePredicateHandlerMapping

WebHandler类 核心逻辑

FilteringWebHandler

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

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

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

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

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

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;

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)

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)

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)方法

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继续执行

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请求到对应的服务器,最终返回结果

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判断是否需要转发到对应地址的

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.rhkb.cn/news/493405.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构漫游记:初识vector

​ 嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的…

go-zero负载均衡实现原理

1. 什么是负载均衡 关于微服务分布式及集群的概念即定义&#xff0c;在业界中这些往往会同时在同一个项目中&#xff0c;而集群在微服务中主要为服务的运行保障高可用。 比如&#xff1a;在当前的项目情况下&#xff0c;我们可能针对用户服务部署两台服务以保障用户服务的高可用…

【Rust自学】4.4. 引用与借用

4.4.0 写在正文之前 这一节的内容其实就相当于C的智能指针移动语义在编译器层面做了一些约束。Rust中引用的写法通过编译器的约束写成了C中最理想、最规范的指针写法。所以学过C的人对这一章肯定会非常熟悉。 喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文…

Apache Solr RCE(CVE-2017-12629)--vulhub

Apache Solr 远程命令执行漏洞&#xff08;CVE-2017-12629&#xff09; Apache Solr 是一个开源的搜索服务器。Solr 使用 Java 语言开发&#xff0c;主要基于 HTTP 和 Apache Lucene 实现。原理大致是文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过 http收到一个…

OpenGL ES 01 渲染一个四边形

项目架构 着色器封装 vertex #version 300 es // 接收顶点数据 layout (location 0) in vec3 aPos; // 位置变量的属性位置值为0 layout (location 1) in vec4 aColors; // 位置变量的属性位置值为1 out vec4 vertexColor; // 为片段着色器指定一个颜色输出void main() {gl…

游戏渠道假量解决方案

某推广公司在推广过程中被查出“短期内点击量激增”“存在同一地址多次访问”“已注册用户重复注册”等数据作弊行为&#xff0c;法院判罚退还服务费200余万元&#xff0c;并赔偿违约金约350万元。 某公司为提升其游戏在应用商店榜单排名&#xff0c;委托某网络公司进行下载、注…

物联网:全面概述、架构、应用、仿真工具、挑战和未来方向

中文论文标题&#xff1a;物联网&#xff1a;全面概述、架构、应用、仿真工具、挑战和未来方向 英文论文标题&#xff1a;Internet of Things: a comprehensive overview, architectures, applications, simulation tools, challenges and future directions 作者信息&#x…

29、基于springboot的网上购物商城系统研发

本课题是根据用户的需要以及网络的优势建立的一个基于Spring Boot的网上购物商城系统&#xff0c;来满足用户网络购物的需求。 本网上购物商城系统应用Java技术&#xff0c;MYSQL数据库存储数据&#xff0c;基于Spring Boot框架开发。在网站的整个开发过程中&#xff0c;首先对…

Spring Boot--06--整合Swagger

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Swagger一、简介官网&#xff1a;https://swagger.io/ Swagger 的优势 二、基本使用1. 导入相关依赖2. 编写配置文件2.1 配置基本信息2.2 配置接口信息2.3 配置分组…

写SQL太麻烦?免费搭建 Text2SQL 应用,智能写 SQL | OceanBase AI 实践

自OceanBase 4.3.3版本推出以来&#xff0c;向量检索的能力受到了很多客户的关注&#xff0c;也纷纷表达希望OB能拓展更多 多模数据库大模型 的AI应用实践。 在上篇文章 &#x1f449; OceanBase LLM&#xff0c;免费构建你的专属 AI 助手 &#xff0c;我们介绍了如何去搭建一…

题海拾贝:力扣 86.分隔链表

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》 欢迎点赞&#xff0c;关注&#xff01; 1、题…

sql server索引优化语句

第一步 建一个测试表 --create table TestUsers --( -- Id int primary key identity(1,1), -- Username varchar(30) not null, -- Password varchar(10) not null, -- CreateDateTime datetime not null --)第二步 插入100w数据 大概1分钟执行时间 ----插入数据…

多智能体/多机器人网络中的图论法

一、引言 1、网络科学至今受到广泛关注的原因&#xff1a; &#xff08;1&#xff09;大量的学科&#xff08;尤其生物及材料科学&#xff09;需要对元素间相互作用在多层级系统中所扮演的角色有更深层次的理解&#xff1b; &#xff08;2&#xff09;科技的发展促进了综合网…

VS Code Copilot 与 Cursor 对比

选手简介 VS Code Copilot&#xff1a;算是“老牌”编程助手了&#xff0c;虽然Copilot在别的编辑器上也有扩展&#xff0c;不过体验最好的还是VS Code&#xff0c;毕竟都是微软家的所以功能集成更好一些&#xff1b;主要提供的是Complete和Chat能力&#xff0c;也就是代码补全…

基于层次化设计方法,设计一个16位二进制全加器

设计思路&#xff1a;先一个半加器和一个或门&#xff0c;每两个半加器构成一个全加器&#xff0c;每四个全加器组成一个4位全加器&#xff0c;再将这4个4位全加器依次相连组成一个16位二进制全加器。 半加器的逻辑表达式&#xff1a;sumAB; coutAB; 半加器的真值表 全加器的逻…

ES搜索原理

ES搜索原理 bg: 搜索的时候&#xff0c;使用模糊查询经常出现搜索不到的情况&#xff0c;不如mysql的like有效。 https://www.bilibili.com/video/BV1yb421J7oX/?spm_id_from333.337.search-card.all.click&vd_source3f917722acc36b0fcca7cca2d21394e2 基础概念 索引&a…

轻松上手:使用 Vercel 部署 HTML 页面教程

&#x1f600; 在学习前端的过程中&#xff0c;部署项目往往是一个令人头疼的问题。然而&#xff0c;Vercel 为我们提供了一个便捷且免费的解决方案。 Vercel 是一个强大的云平台&#xff0c;专门用于前端项目的部署和托管。它不仅支持多种前端框架和静态网站生成器&#xff0…

STL 剖析

STL 六大组件 「STL 六大组件的交互关系」 Container 通过 Allocator 取得数据储存空间Algorithm 通过 Iterator 存取 Container 内容Functor 可以协助 Algorithm 完成不同的策略变化Adapter 可以修饰或套接 Functor、Iterator 配置器(allocator) 配置器&#xff1a;负责空间…

HTTP—03

触发 GET 请求 1&#xff09;直接在浏览器 地址栏 输入 URL&#xff0c;此时构成了一个GET请求 2&#xff09;HTML中的一些特殊标签可能会触发 例如 img,a,link,script... 3&#xff09;通过Form表单触发&#xff08;Form本质也是一个HTML标签&#xff09; 4&#xff0…

C 数组:索引魔杖点化的数据星图阵列

一、数组 1.数组的概念 数组是⼀组相同类型元素的集合&#xff1b;从这个概念中我们就可以发现2个有价值的信息&#xff1a; 数组中存放的是1个或者多个数据&#xff0c;但是数组元素个数不能为0。数组中存放的多个数据&#xff0c;类型是相同的。 2.数组的分类 数组主要分为一…