【深入解析spring cloud gateway】06 gateway源码简要分析
上一节做了一个很简单的示例,微服务通过注册到eureka上,然后网关通过服务发现访问到对应的微服务。本节将简单地对整个gateway请求转发过程做一个简单的分析。
一、核心流程

主要流程:
- Gateway Client向 Spring Cloud Gateway 发送请求
- 请求首先会被HttpWebHandlerAdapter 进行提取组装成网关上下文
- 然后网关的上下文会传递到DispatcherHandler ,它负责将请求分发给 RoutePredicateHandlerMapping
- RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用
- 如果过断言成功,由FilteringWebHandler 创建过滤器链并调用
- 通过特定于请求的 Fliter 链运行请求,Filter 被虚线分隔的原因是Filter可以在发送代理请求之前(pre)和之后(post)运行逻辑
- 执行所有pre过滤器逻辑。然后进行代理请求。发出代理请求后,将运行“post”过滤器逻辑。
- 处理完毕之后将 Response 返回到 Gateway 客户端
二、具体分析
请求过来,会经过HttpWebHandlerAdapter.handle方法,可以理解为这就是请求的主入口
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {if (this.forwardedHeaderTransformer != null) {try {request = this.forwardedHeaderTransformer.apply(request);}catch (Throwable ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to apply forwarded headers to " + formatRequest(request), ex);}response.setStatusCode(HttpStatus.BAD_REQUEST);return response.setComplete();}}//组装上下文ServerWebExchange exchange = createExchange(request, response);LogFormatUtils.traceDebug(logger, traceOn ->exchange.getLogPrefix() + formatRequest(exchange.getRequest()) +(traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : ""));
//委派给delegate来处理return getDelegate().handle(exchange).doOnSuccess(aVoid -> logResponse(exchange)).onErrorResume(ex -> handleUnresolvedError(exchange, ex)).then(Mono.defer(response::setComplete));
}
这一个delegate是个啥,看一下接口定义:
就是一个处理器,所有参数封装在上下文exchange中
public interface WebHandler {/*** Handle the web server exchange.* @param exchange the current server exchange* @return {@code Mono<Void>} to indicate when request handling is complete*/Mono<Void> handle(ServerWebExchange exchange);}
最终会调到DispatcherHandler
@Override
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).concatMap(mapping -> mapping.getHandler(exchange)).next().switchIfEmpty(createNotFoundError()).flatMap(handler -> invokeHandler(exchange, handler)).flatMap(result -> handleResult(exchange, result));
}
handlerMappings是啥
是一个列表,HandlerMapping可以根据当前请求,找到对应的处理器
如果当前请求比如/hello/world,在gateway服务上是一个controller对应的接口,那这个就可以通过RequestMappingHandlerMapping找到一个RequestMappingHandlerAdapter。
如果当前请求,是需要转发给下游微服务的,则找到RoutePredicateHandlerMapping

RoutePredicateHandlerMapping查找路由主要逻辑
@Override
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().getLocalAddress() != null&& exchange.getRequest().getLocalAddress().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上下文中exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);//返回的handler实际上是webHandlerreturn 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) + "]");}})));
}
看下查找路由的具体方式:原来是将所有的路由,用predicate作一下匹配,找出符合当前请求的路由
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 testingexchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());//用predicate作一下匹配,找出符合当前请求的路由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()); }*/
}
routeLocator包含了哪些路由,Debug看一下

可以看到,用了服务注册和发现后,实际上,一个微服务会自动注册一个路由,比如上面的hello-service,自动注册了一个路径为:/hello-service/**的路由。这就是为什么我们yml配置文件中明明什么路由也没有配置,也能自动转发hello-service的请求。
同时,可以看到,这个路由下面有一个ReWritePathFilter,会自动去掉服务名,将请求转发给下游微服务。

下一步再看看FilteringWebHandler中的处理逻辑
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
//从上下文中取出路由,路由中包含filtersRoute route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);List<GatewayFilter> gatewayFilters = route.getFilters();
//spring容器中的Global Filter也取出来List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);combined.addAll(gatewayFilters);// TODO: needed or cached?//做个排序AnnotationAwareOrderComparator.sort(combined);if (logger.isDebugEnabled()) {logger.debug("Sorted gatewayFilterFactories: " + combined);}
//后面就是filter链式调用了return new DefaultGatewayFilterChain(combined).filter(exchange);
}
可以看到代码中主要有两个逻辑:
1、从上下文中取出路由,路由中包含filters
2、spring容器中的Global Filter也取出来
3、合并上面的两种filter,并且排序
4、filters列表组装成责任链来进行调用
可以通过源码,再来看看核心流程的那个图,这样就比较清楚了。
总结了另一个稍微详细一点的图:

个人看这块源码的体会:整个核心流程并不复杂,难点大概是reactor响应式编程,如果之前没接触过这一块,那么看着会有种不知道下一步往哪里的困惑!所以要学习这块源码,还得学习reactor。
相关文章:
【深入解析spring cloud gateway】06 gateway源码简要分析
上一节做了一个很简单的示例,微服务通过注册到eureka上,然后网关通过服务发现访问到对应的微服务。本节将简单地对整个gateway请求转发过程做一个简单的分析。 一、核心流程 主要流程: Gateway Client向 Spring Cloud Gateway 发送请求请求…...
2023年行研行业研究报告
第一章 行业概述 1.1 行研行业 行业定义为同一类别的经济活动,这涉及生产相似产品、应用相同生产工艺或提供同类服务的集合,如食品饮料行业、服饰行业、机械制造行业、金融服务行业和移动互联网行业等。 为满足全球金融业的需求,1999年8月…...
linux上vscode中.cpp文件中引入头文件.hpp时报错:找不到头文件(启用错误钵形曲线)
当在.cpp文件中引入系统给定的头文件时:#include < iostream > 或者引入自定义的头文件 :#include <success.hpp> 报错:找不到相应的头文件,即在引入头文件的改行底下标出红波浪线 解决方法为: &#…...
Sphinx Docstring
入门 — Sphinx documentation pip install sphinx pip install sphinx-rtd-themesphinx-quickstartexport PYTHONPATH"-"make html cd build/htmlpython -m http.server 9121nohup python -m http.server 9121 &...
JVM的故事——虚拟机类加载机制
虚拟机类加载机制 文章目录 虚拟机类加载机制一、概述二、类加载的时机三、类加载的过程四、类加载器 一、概述 本章将要讲解class文件如何进入虚拟机以及虚拟机如何处理这些class文件。Java虚拟机把class文件加载到内存,并对数据进行校验、转换解析和初始化&#…...
Sentry 是一个开源的错误监控和日志聚合平台-- 通过docker-compose 安装Sentry
概述 Sentry 是一个开源的错误监控和日志聚合平台,用于帮助开发团队实时监控和调试应用程序中的错误和异常。它可以捕获应用程序中的错误和异常,并提供详细的错误报告,包括错误堆栈跟踪、环境信息、用户信息等。这些报告可以帮助开发团队快速…...
Redis 7 第六讲 主从模式(replica)架构篇
🌹🌹🌹 此篇开始进入架构篇范围(❤艸`❤) 理论 即主从复制,master以写为主,Slave以读为主。当master数据变化的时候,自动将新的数据异步同步到其它slave数据库。 使用场景 读写分离 容灾备份数据备份水平扩容主从架构 演示案例 注:masterauth、replicaof主…...
学习资源记录 =0=
学习路线: 无人机学习路线 无人机学习路线2 自主无人机: 浙大fastlab无人机 机器人理论: 华中科技大学机器人学 C课程 机器人仿真: 2023gazebo仿真开发四足机器人...
Python import包路径管理
import sys sys.path.insert(0, "../")详细链接...
OB Cloud助力泡泡玛特打造新一代分布式抽盒机系统
作为中国潮玩行业的领先者,泡泡玛特凭借 MOLLY、DIMOO、SKULLPANDA 等爆款 IP,以及线上线下全渠道营销收获了千万年轻人的喜爱,会员数达到 2600 多万。2022 年,泡泡玛特实现 46.2 亿元营收,其中线上渠道营收占比 41.8%…...
Linux socket网络编程实战(tcp)实现双方聊天
在上节已经系统介绍了大致的流程和相关的API,这节就开始写代码! 回顾上节的流程: 创建一个NET文件夹 来存放网络编程相关的代码: tcp服务端代码初步实现--上 这部分先实现服务器的连接部分的代码并进行验证 server1.cÿ…...
BuhoCleaner for mac:让你的Mac重获新生
你是否曾经因为电脑运行缓慢而感到困扰?是否曾经因为大量的垃圾文件和无效的临时文件而感到头疼?如果你有这样的烦恼,那么BuhoCleaner for mac就是你的救星! BuhoCleaner for mac是一款专门为Mac用户设计的系统清理工具ÿ…...
陶氏公司将出席2023第二届中国汽车碳中和峰会
2023第二届中国汽车碳中和峰会将于10月19日-20日在上海举办。 本次峰会将为行业领导者、政策制定者和专家提供一个平台,讨论汽车行业减少碳排放的策略。专家们将从政策、供应链、ESG、替代能源解决方案、汽车材料创新、法律等不同领域分享碳中和与可持续策略。 通…...
【linux命令讲解大全】051.Linux Awk脚本语言中的字段定界符和流程控制
文章目录 设置字段定界符流程控制语句条件判断语句循环语句while语句for循环do循环 其他语句 数组应用数组的定义读取数组的值数组相关函数二维、多维数组使用 从零学 python 设置字段定界符 默认的字段定界符是空格,可以使用-F “定界符” 明确指定一个定界符&…...
Gradle下载安装教程
1、Gradle 入门 1.1、Gradle 简介 Gradle 是一款Google 推出的基于 JVM、通用灵活的项目构建工具,支持 Maven,JCenter 多种第三方仓库;支持传递性依赖管理、废弃了繁杂的xml 文件,转而使用简洁的、支持多种语言(例如:java、groo…...
Python 之 match 表达式
Python 从 3.10 版本开始增加了 match 语句,和其他语言常见的 switch 语句极其相似,但功能更加强大。 本文通过实例,了解下其用法。 基本的 match 语句 def http_code(status): match status: case 400 | 404 | 418: …...
.NET Framework 微软官方下载地址
微软官方下载地址: 下载 .NET Framework | 免费官方下载 (microsoft.com) 版本发布日期终止支持.NET Framework 4.8.12022年8月9日.NET Framework 4.82019年4月18日.NET Framework 4.7.22018年4月30日.NET Framework 4.7.12017年10月17日.NET Framework 4.72017年…...
OpenCV(十四):ROI区域截取
在OpenCV中,你可以使用Rect对象或cv::Range来截取图像的感兴趣区域(Region of Interest,ROI)。 方法一:使用Rect对象截取图像 Rect_(_Tp _x, _Tp _y, _Tp _width,_Tp _height) Tp:数据类型&…...
Java学习笔记之----I/O(输入/输出)一
在变量、数组和对象中存储的数据是暂时存在的,程序结束后它们就会丢失。想要永久地存储程序创建的数据,就需要将其保存在磁盘文件中(就是保存在电脑的C盘或D盘中),而只有数据存储起来才可以在其他程序中使用它们。Java的I/O技术可…...
介绍GitHub
GitHub 是一个基于互联网的源代码托管平台,可以帮助软件开发者存储和管理源代码,方便团队协作和版本控制。GitHub 的主要功能包括: 代码托管:开发者可以在 GitHub 上创建远程代码仓库,存储和管理他们的源代码。 版本控…...
为什么选择Drawflow:5大优势让你爱上这个流程图库
为什么选择Drawflow:5大优势让你爱上这个流程图库 【免费下载链接】Drawflow Simple flow library 🖥️🖱️ 项目地址: https://gitcode.com/gh_mirrors/dr/Drawflow Drawflow是一个简单而强大的JavaScript流程图库,专为创…...
Windows加域必看:如何用PowerShell一键指定OU路径(附完整代码)
Windows域管理自动化:PowerShell指定OU路径的终极指南 在大型企业IT环境中,计算机加域操作从来不是单次事件,而是需要批量执行的常规运维任务。传统手动操作不仅效率低下,还容易因人为失误导致计算机被放入错误的组织单元(OU)。想…...
利用MathType公式与GLM-OCR结合实现理科试卷自动批改
利用MathType公式与GLM-OCR结合实现理科试卷自动批改 1. 引言 批改理科试卷,尤其是数学、物理这类包含大量公式和符号的试卷,对老师来说一直是个耗时费力的活儿。一张试卷,既要看文字答案对不对,又要检查复杂的公式推导有没有写…...
基于python的演唱会门票演出购票系统的设计与实现
目录同行可拿货,招校园代理 ,本人源头供货商用户管理模块演出信息管理购票与选座功能支付系统集成订单与票务管理数据分析与报表高并发优化项目技术支持源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作同行可拿货,招校园代理 ,本人源头供货商…...
Proteus仿真实战:基于STM32的智能环境感知与联动控制系统(附源码)
1. 项目背景与核心功能 想象一下这样的场景:当你走进书房时,灯光自动亮起;当室内温度过高时,空调自动开启;当光线不足时,窗帘缓缓拉开。这些看似科幻的场景,其实用STM32单片机和Proteus仿真就能…...
丹青幻境功能全解析:宣纸UI、动态LoRA、文艺交互实操
丹青幻境功能全解析:宣纸UI、动态LoRA、文艺交互实操 1. 数字艺术创作新范式 在数字艺术创作领域,丹青幻境Z-Image Atelier带来了一场界面革命。这款工具将4090显卡的强大算力隐藏在仿古宣纸界面背后,为创作者提供了前所未有的沉浸式体验。…...
Qwen2.5-VL图文助手体验:RTX 4090极速推理,支持对话历史和一键清空
Qwen2.5-VL图文助手体验:RTX 4090极速推理,支持对话历史和一键清空 如果你手头有一张RTX 4090显卡,想找一个能看懂图片、能聊天、还能帮你处理各种视觉任务的本地AI助手,那么今天要聊的这个工具,你可能会很感兴趣。 …...
B站成分检测器:3分钟快速识别评论区同好身份
B站成分检测器:3分钟快速识别评论区同好身份 【免费下载链接】bilibili-comment-checker B站评论区自动标注成分油猴脚本,主要为原神玩家识别 项目地址: https://gitcode.com/gh_mirrors/bi/bilibili-comment-checker 还在为B站评论区难以分辨用户…...
别再手动转格式了!用Python的docx2pdf库5行代码搞定Word转PDF(Windows/Mac通用教程)
5行代码终结格式转换焦虑:Python自动化Word转PDF全攻略 每次市场部门催着要电子合同时,你是不是还在手忙脚乱地点击"另存为PDF"?当运营团队需要批量生成上百份产品手册时,是否还在忍受重复机械的格式转换操作࿱…...
基于Comsol相控阵技术的实用钢纵波超声波成像模型:单层缺陷TFM成像与压力声学仿真
comsol 相控阵 超声成像 此模型为压力声学仿真超声波,实用钢纵波速度6000 密度7.8e-9 单层缺陷TFM成像相控阵超声检测这玩意儿在工业NDT圈子里算是老熟人了,今天咱们拿COMSOL搞个钢材料缺陷成像的骚操作。模型基础是压力声学模块,材料参数先给…...
