【深入解析spring cloud gateway】06 gateway源码简要分析

news2024/11/19 23:27:25

上一节做了一个很简单的示例,微服务通过注册到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 port
    if (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实际上是webHandler
                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) + "]");
                }
            })));
}

看下查找路由的具体方式:原来是将所有的路由,用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 testing
                exchange.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) {
//从上下文中取出路由,路由中包含filters
    Route 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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/971572.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

普通用户使用spark的client无法更新Ranger策略

普通用户使用spark的client无法更新Ranger策略 报错图片&#xff1a; WARN org.apache.ranger.admin.client.RangerAdminRESTClient: Error getting Roles. secureModetrue, usercaojianxiangUCDIPA.VIATRIS.CC (auth:KERBEROS)&#xff0c;responsef"httpStatusCode&quo…

ToBeWritten之基于ATTCK的模拟攻击:闭环的防御与安全运营

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…

react使用hook封装一个tab组件

目录 react使用hook封装一个tab组件Tabbar.jsx使用组件效果 react使用hook封装一个tab组件 Tabbar.jsx import PropsTypes from "prop-types"; import React, { useEffect, useState } from react; export default function Tabbar(props) {const { tabData , cur…

华为云银河麒麟V10安装libmcrypt

本次安装是在华为云上执行。cpu是鲲鹏&#xff0c;操作系统是银河麒麟V10. 先下载安装包&#xff1a; wget http://downloads.sourceforge.net/mcrypt/libmcrypt-2.5.8.tar.gz 解包&#xff0c;进入目录中。 执行如下命令&#xff1a; ./configure make make install 执…

软件开发模型汇总

1. 软件模型 在计算机刚刚诞生的年代&#xff0c;计算机是一种只有天才才能掌握的工具。人们对计算机的认知仅仅停留在程序的层面上&#xff0c;所谓的软件开发就是这些能够掌握计算机的天才们写的一些只能计算的二进制序列而已。但是随着技术的发展&#xff0c;软件的复杂度不…

K8S:二进制部署K8S(两台master+负载均衡nginx+keepalived)

文章目录 一.常见的K8S部署方式1.Minikube2.Kubeadmin3.二进制安装部署 二.二进制搭建K8S(双台master)1.部署架构规划2.系统初始化配置3.部署 docker引擎4.部署 etcd 集群&#xff08;1&#xff09;etcd简介&#xff08;2&#xff09;准备签发证书环境&#xff08;3&#xff09…

CNN详细讲解

CNN(Convolutional Neural Network) 本文主要来讲解卷积神经网络。所讲解的思路借鉴的是李宏毅老师的课程。 CNN&#xff0c;它是专门被用在影像上的。 Image Classification 我们从影像分类开始说起。 我们举例来说&#xff0c;它固定的输入大小是100*100的解析度&#x…

初识Maven(一)命令行操作和idea创建maven工程

Maven 是 Apache 软件基金会组织维护的一款专门为 Java 项目提供**构建**和**依赖**管理支持的工具。 构建过程包含的主要的环节&#xff1a;- 清理&#xff1a;删除上一次构建的结果&#xff0c;为下一次构建做好准备 - 编译&#xff1a;Java 源程序编译成 *.class 字节码文件…

服务端请求伪造(SSRF)

文章目录 渗透测试漏洞原理服务端请求伪造1. SSRF 概述1.1 SSRF 场景1.1.1 PHP 实现 1.2 SSRF 原理1.3 SSRF 危害 2. SSRF 攻防2.1 SSRF 利用2.1.1 文件访问2.1.2 端口扫描2.1.3 读取本地文件2.1.4 内网应用指纹识别2.1.5 攻击内网Web应用 2.2 SSRF 经典案例2.2.1 访问页面2.2.…

电脑怎么设置定时关机,2个简单的操作

电脑作为现代生活中不可或缺的工具&#xff0c;我们通常会在工作或娱乐过程中使用它。但有时候&#xff0c;我们可能需要在一段时间后自动关机&#xff0c;例如在下载完成后或在睡觉前。那么电脑怎么设置定时关机呢&#xff1f;为了满足这种需求&#xff0c;电脑提供了多种定时…

2023年铁路行业研究报告

第一章 行业概况 1.1 定义 铁路行业&#xff0c;依照全球行业分类标准&#xff08;GICS&#xff09;的划分&#xff0c;被归属为交通运输行业的一部分。该行业主要由提供乘客和货物铁路运输服务的公司构成。铁路行业涵盖各类铁路运输服务&#xff0c;包括城市铁路系统&#x…

32 实验三十二、OCL电路的研究

一、题目 仿真电路如图1所示。利用 Multisim 研究下列问题&#xff1a; &#xff08;1&#xff09;负载 R 6 R_6 R6​ 上能获得的最大输出功率&#xff1b; &#xff08;2&#xff09;电容 C 1 C_1 C1​、 C 2 C_2 C2​ 的作用&#xff1b; &#xff08;3&#xff09;当输入…

QTday5(QT连接TCP通信)

一、Xmind整理&#xff1a; C语言中的通信协议&#xff1a; 二、上课笔记整理&#xff1a; 1.QT中的服务器端的操作&#xff1a; .pro文件&#xff1a; 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务…

OpenCV:实现图像的负片

负片 负片是摄影中会经常接触到的一个词语&#xff0c;在最早的胶卷照片冲印中是指经曝光和显影加工后得到的影像。负片操作在很多图像处理软件中也叫反色&#xff0c;其明暗与原图像相反&#xff0c;其色彩则为原图像的补色。例如&#xff0c;颜色值A与颜色值B互为补色&#…

微信小程序slot插槽的介绍,以及如何通过uniapp使用动态插槽

微信小程序文档 - slots介绍 由上述文档看俩来&#xff0c;微信小程序官方并没有提及动态插槽内容。 uniapp文档 - slots介绍 uni官方也未提及关于动态插槽的内容 在实际使用中&#xff0c;直接通过 <<slot :name"item.xxx" /> 这种形式会报错&#xff…

数据库-多表设计

概述&#xff1a; 项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xff0c;基本分为三种&#xff1a; 一对…

信息系统安全运维和管理指南

声明 本文是学习 信息系统安全运维管理指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 安全运维支撑系统 信息系统安全服务台 目的 对信息系统安全事件进行统一监控与处理。 要求 建立一个集中的信息系统运行状态收集、处理、显示及报警的系…

〔021〕Stable Diffusion 之 提示词反推、自动补全、中文输入 篇

✨ 目录 &#x1f388; 反推提示词 / Tagger&#x1f388; 反推提示词 Tagger 使用&#x1f388; 英文提示词自动补全 / Booru tag&#x1f388; 英文提示词自动补全 Booru tag 使用&#x1f388; 中文提示词自动补全 / tagcomplete&#x1f388; 中文提示词自动补全 tagcomple…

AI「反腐」,德国马普所结合 NLP 和 DNN 开发抗蚀合金

内容一览&#xff1a;在被不锈钢包围的世界中&#xff0c;我们可能都快忘记了腐蚀的存在。然而&#xff0c;腐蚀存在于生活中的方方面面。无论是锈迹斑斑的钢钉&#xff0c;老化漏液的电线&#xff0c;还是失去光泽的汽车&#xff0c;这一切的发生都与腐蚀有关。据统计&#xf…

uni-app 之 tabBar 底部切换按钮

uni-app 之 tabBar 底部切换按钮 1693289945724.png {"pages": [ //pages数组中第一项表示应用启动页&#xff0c;参考&#xff1a;https://uniapp.dcloud.io/collocation/pages{"path": "pages/home/home","style": {"navigatio…