SpringCloud源码探析(五)-网关Gateway的使用

news2024/11/19 13:14:18

1.概述

在微服务架构中,网关提供了统一的对外访问入口(自身跨一个或多个服务),它保证了内部服务对外暴露的合理性与安全性、降低了服务之间访问的复杂性,是微服务架构中至关重要的一部分。在SpringCloud中网关主要包含两种:Zuul和Gateway,Zuul是基于Servlet实现的,属于阻塞式编程,而SpringCloud Gateway是基于Spring 5中提供的WebFlux,属于响应式编程的实现,具有更好的性能。本文将详细分析SpringCloud Gateway的使用以及其实现原理。

2.Gateway使用

2.1 网关作用

在微服务架构中,网关所处的位置如下图所示:
在这里插入图片描述
网关的主要功能如下:

(1)权限校验:校验用户身份,合法用户允许访问;
(2)路由转发:制定路由规则,根据路由映射到指定服务;
(3)请求限流:控制访问流量,当请求数量达到一定值时,限制部分访问;
(4)API监控:监控应用程序请求结果、请求数量等。

网关的优势在于:
(1)统一入口:为全部微服务提供一个唯一的入口,网关起到外部和内部的隔离作用,保障了后台服务的安全性;
(2)鉴权校验:识别每个请求的权限,拒绝不符合条件的请求;
(3)动态路由:将请求路由到不同的后台微服务中;
(4)减少耦合:减少客户端与服务端的耦合,服务可以独立发展,通过网关来做映射。

2.2 Gateway路由核心元素

在这里插入图片描述
Gateway在启动时会创建Netty Server,它会接收来自客户端的请求,收到请求后会根据路由规则匹符合条件的路由,请求会被该路由配置的过滤器依次进行拦截处理,再由Netty Client转移到服务目标,由微服务处理后返回,返回结果会再次被过滤器处理,最后才会返回给Client。这里涉及Route的核心概念如下:

  • Route:一个Gateway服务可以配置多个Route,一个Route由ID、转发URI和多个Predicates 、Filters 构成。处理请求时会按照优先级排序,找到第一个满足所有Predicates 的 Route;
  • Predicate:路由断言,判断请求是否符合要求,符合则转发到路由目的地(能够根据多种属性匹配:请求路径、方法、请求头header等)。一个Route可以包含多个Predicates;
  • Filter:过滤器包括了处理请求和响应的逻辑,可以分为 pre 和 post 两个阶段,PreFilter(请求前处理)可以做参数校验、流量监控、日志记录、修改请求内容等等,在PostFilter(请求后处理)可以做响应内容修改。Gateway 包括两类 Filter:全局 Filter、路由Filter。每种全局 Filter 全局只会有一个实例,会对所有的 Route 都生效。路由 Filter 是针对 Route进行配置的,不同的 Route 可以使用不同的参数,因此会创建不同的实例。

2.3 Gateway使用

2.3.1 引入pom

       <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>

2.3.2 添加配置

spring:
  cloud:
    gateway:
      routes:
        - id: userservice
          uri: lb://userservice
          predicates:
            - Path=/user/**
        - id: orderservice
          uri: lb://orderservice
          predicates:
            - Path=/order/**

2.3.3 启动类

@Slf4j
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        log.info("这是新的GatewayApplication");
        SpringApplication.run(GatewayApplication.class, args);
    }
}

2.3.4 运行结果

在这里插入图片描述
在这里插入图片描述

2.4 Gateway运行原理

2.4.1 路由原理

在这里插入图片描述
Gateway接收请求的流程如上图所示:

1.请求会先经过DispatcherHandler;
2.DispatcherHandler会根据RoutePredicateHandlerMapping获取Handler方法;
3.RoutePredicateHandlerMapping需要根据RouteLocator获取所有路由配置信息并匹配满足条件的路由;
4.RoutePredicateHandlerMapping会将请求交给FilteringWebHandler处理;
5.FilteringWebHandler从已经匹配的路由中获取对应的路由Filter,与全局Filter合并构造GatewayFilterChain,最终由GatewayFilterChain里的Filter按照顺序进行处理。

2.4.2 RouteLocator

一个Route主要包含以下几个属性:

属性名称含义
Id路由Id,具有唯一性
uri转发请求的目标地址
Order优先级
Predicate规则(匹配条件),多个规则会合并成一个聚合条件
Filters路由过滤器,这些过滤器与全局过滤器一起,按顺序处理请求并按要求返回结果
Metadata额外的元数据

Gateway主要通过接口RouteLocator接口来获取路由配置,RouteLocator代码如下:

public interface RouteLocator {

	Flux<Route> getRoutes();

}

在Gateway的源码中,RouteLocator的实现类主要有:RouteDefinitionRouteLocator、CompositeRouteLocator、CachingRouteLocator。它们之间的关系如下图所示:
在这里插入图片描述

RouteDefinitionLocator 提供RouteDefinition ,由 RouteDefinition 构造路由。可以存在多个RouteLocator ,这些实例提供的路由最终会由 CompositeRouteLocator 整合,再由 CachingRouteLocator 缓存。CachingRouteLocator 会把下层的 RouteLocator 返回的路由缓存起来,后续直接返回使用,同时监听RefreshRoutesEvent来定时刷新缓存的路由。

2.4.3 Predicate

Predicate接口提供了可以针对请求条件的匹配规则,匹配成功的路由负责处理该请求。路由是分优先级的,匹配时会根据优先级返回第一个匹配成功的路由,配置时匹配条件越具体(越详尽)的路由应配置更高的优先级,优先级从-2^31 到 2^31-1,值越小优先级越高。Gateway提供了很多基于Predicate的规则,比如Header、Host、Parameter、Request Method等,也可以实现接口RoutePredicateFactory自定义规则,RoutePredicateFactory接口代码如下:

@FunctionalInterface
public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {
	// ...
   
  Predicate<ServerWebExchange> apply(C config);

  // ...
}

2.4.4 Filter

Gateway过滤器分为两种,全局过滤器和路由过滤器,全局 Filter会自动对所有的路由都生效,有些功能比如全局日志、请求时长等就比较适合设计成全局过滤器,这类过滤器对应的是 GlobalFilter 接口,自定义全局过滤器只需要实现该接口并注册到spring容器即可。

public interface GlobalFilter {

	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

}

路由Filter是针对某个具体的路由进行配置,需要实现接口 GatewayFilterFactory,路由Filter主要针对具体路由进行不同配置,比如添加删减请求头参数、添加鉴权信息等。代码如下:

@FunctionalInterface
public interface GatewayFilterFactory<C> extends ShortcutConfigurable, Configurable<C> {
	// ...
	
	GatewayFilter apply(C config);

	// ...
}

上述两类 Filter 在处理请求之前会先放一起排序,通过Order注解或者实现 Ordered 接口声明进行声明,Gateway Filter 处理请求和响应相关的核心代码在 FilteringWebHandler 类,代码如下:

public class FilteringWebHandler implements WebHandler {
    protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class);
    private final List<GatewayFilter> globalFilters;

    public FilteringWebHandler(List<GlobalFilter> globalFilters) {
        this.globalFilters = loadFilters(globalFilters);
    }

    private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
        return (List)filters.stream().map((filter) -> {
            FilteringWebHandler.GatewayFilterAdapter gatewayFilter = new FilteringWebHandler.GatewayFilterAdapter(filter);
            if (filter instanceof Ordered) {
                int order = ((Ordered)filter).getOrder();
                return new OrderedGatewayFilter(gatewayFilter, order);
            } else {
                return gatewayFilter;
            }
        }).collect(Collectors.toList());
    }

    public Mono<Void> handle(ServerWebExchange exchange) {
        Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        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);
        }

        return (new FilteringWebHandler.DefaultGatewayFilterChain(combined)).filter(exchange);
    }

    private static class GatewayFilterAdapter implements GatewayFilter {
        private final GlobalFilter delegate;

        GatewayFilterAdapter(GlobalFilter delegate) {
            this.delegate = delegate;
        }

        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return this.delegate.filter(exchange, chain);
        }

        public String toString() {
            StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
            sb.append("delegate=").append(this.delegate);
            sb.append('}');
            return sb.toString();
        }
    }

    private static class DefaultGatewayFilterChain implements GatewayFilterChain {
        private final int index;
        private final List<GatewayFilter> filters;

        DefaultGatewayFilterChain(List<GatewayFilter> filters) {
            this.filters = filters;
            this.index = 0;
        }

        private DefaultGatewayFilterChain(FilteringWebHandler.DefaultGatewayFilterChain parent, int index) {
            this.filters = parent.getFilters();
            this.index = index;
        }

        public List<GatewayFilter> getFilters() {
            return this.filters;
        }

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

由上述代码可知, FilteringWebHandler 会将所有的 GlobalFilter 实例加载进来并使用GatewayFilterAdapter 适配成 GatewayFilter。在handle()方法处理请求时,会将适配后的GlobalFilter 以及路由GatewayFilter合并在一个List中,根据Order进行排序,排序之后会构造一个GatewayFilterChain,由其中 filter() 方法触发这些 Filter 的执行。

3.小结

1.Gateway包含的核心元素主要有:Route、Predicate、Filter;
2.Gateway中路由过滤器分为全局过滤器和路由过滤器,全局过滤器和路由过滤器会根据Order排序执行;
3.Gateway主要通过接口RouteLocator接口来获取路由配置。

4.参考文献

1.https://www.bilibili.com/video/BV1LQ4y127n4
2.https://juejin.cn/post/6844903788680052750
3.https://spring.io/projects/spring-cloud-gateway

5.附录

https://gitee.com/Marinc/nacos.git

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

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

相关文章

让观众近距离感受如然之光 lipro智能家居成上海车展亮点之一

近段时间&#xff0c;上海国际车展如火如荼地举行&#xff0c;吸引了众多汽车厂商及相关企业参展。在本次展会上&#xff0c;星纪魅族集团也携手亿咖通科技&#xff0c;以联合参展的形式登陆「上海国际车展」 6.1号馆&#xff0c;共同展示智能汽车解决方案及产品创新。此外&…

第十四届蓝桥杯研究生组2023年省赛题解--全部采用Java语言实现

引言 今天现在这里 挖个坑&#xff0c;太忙了&#xff0c;这个专题不一样有时间补完&#xff0c;但我会尽力而为的。记录一下今天的日子&#xff1a;2023/04/21 看看这个坑要什么时候自己才能补完。 题目pdf下载&#xff1a;第十四届蓝桥杯研究生组pdf下载 在此特别感谢博主…

flac转换成mp3,4种方法教会你

怎么把flac转换成mp3&#xff1f;由于互联网技术的不断提升&#xff0c;我们每天接触到的各种音频格式变得越来越多样化。然而&#xff0c;有些特殊格式的音频会在处理时带来一定困难&#xff0c;因为它们的兼容性较差&#xff0c;不能在常用播放器上正常播放&#xff0c;这就给…

React面试题

React 如何创建一个react的项目&#xff08;使用脚手架&#xff09; 安装cr脚手架&#xff1a;npm install -g create-react-app进入文件夹&#xff1a;create-react-app 项目名称进入项目&#xff1a;cd 项目名称运行项目&#xff1a;npm start 如何不使用脚手架创建一个项…

maven打包学习

这篇文章不错&#xff1a; 思考&#xff1a;打包的时候是按照当前项目为标准打包的&#xff0c;那么所有的冲突都会以当前pom为根pom打包。那么&#xff0c;如果jar冲突&#xff0c;会用当前pom的依赖jar。 根据这个文章做了个有趣的实验&#xff1a; object3 //install 2个…

深度强化学习——第一次知识小结(3.5)

一、策略网络的小结&#xff1a; 重要概念回顾&#xff1a; 1、动作价值函数QΠ(st,at) 动作价值函数是未来奖励总和Ut的条件期望&#xff0c;如果已知了策略函数Π与当前的状态st&#xff0c;QΠ就可以对所有的动作a打分&#xff0c;以此来决定选择哪个a 其实顾名思义就是…

2023第14届蓝桥杯C/C++A组省赛题解

省一了&#xff0c;占个坑&#xff0c;今天晚上来补蓝桥的题 试题 A: 幸运数 本题总分&#xff1a;5 分 【问题描述】 小蓝认为如果一个数含有偶数个数位&#xff0c;并且前面一半的数位之和等于后面 一半的数位之和&#xff0c;则这个数是他的幸运数字。例如 2314 是…

ROC的理解

ROC 的由来 ROC 曲线是由混淆矩阵衍生来的指标。 混淆矩阵如图所示&#xff0c; 二ROC曲线的横坐标为 FPR&#xff0c;纵坐标为 TPR&#xff0c;计算公式分别是 F P R F P F P T N , 也就是 F P R F P F A L S E FPR \frac{FP}{FPTN}, 也就是 FPR \frac{FP}{FALSE} FP…

5天学会Linux C高级

day1 用C语言的理论知识点去推断结果 需求&#xff1a;让面试官知道你懂这个内容 一、C语言补充内容 【1】结构体补充内容&#xff1a; 1&#xff09;结构体.等法 结构体.等法代码 #include <stdio.h> struct student { int num; float score; char name[32…

docker镜像创建|实战案例(ssh|systemd|nginx|apache)

docker镜像创建|实战案例&#xff08;ssh|systemd|nginx|apache&#xff09; 一 Docker 镜像的创建1.基于现有镜像创建2基于本地模板创建3 基于Dockerfile 创建为什么Docker里的centos的大小才200M&#xff1f;DockerfileDocker 镜像结构的分层 4 Dockerfile 案例&#xff08;h…

Java IO数据流

Java IO数据流介绍 在程序开发中&#xff0c;将不同输入/输出设备&#xff08;例如文件、网络、压缩包等&#xff09;之间的数据传输抽象为流。可以将流分为字节流&#xff08;以Stream结尾的流&#xff09;和字符流&#xff08;以Reader和Writer结尾的流&#xff09;两种 try…

水羊转债,超达转债,晓鸣转债上市价格预测

水羊转债 基本信息 转债名称&#xff1a;水羊转债&#xff0c;评级&#xff1a;A&#xff0c;发行规模&#xff1a;6.94987亿元。 正股名称&#xff1a;水羊股份&#xff0c;今日收盘价&#xff1a;13.94元&#xff0c;转股价格&#xff1a;13.71元。 当前转股价值 转债面值 /…

5.3 Mybatis映射文件 - 零基础入门,轻松学会查询的select标签和resultMap标签

本文目录 前言一、创建XML映射文件二、MybatisX插件安装三、mapper标签四、select标签UserMapper接口方法UserMapper.xml 五、resultMap标签定义resultMap标签修改select标签 总结 前言 MyBatis的强大在于它的语句映射&#xff0c;它提供了注解和XML映射文件两种开发方式&…

jenkins——邮件告警

文章目录 一、下载 Email Extension 插件二、修改系统配置项目1、Jenkins Location配置2、邮件通知配置&#xff08;用于测试连通性&#xff09;3、Extended E-mail Notification配置4、Jenkins Location 配置5、在 job 中配置邮件发送6、高级设置7、配置发送 trigger8、构建验…

arduino学习笔记3

一.RGB三色小灯实验 1.源代码 int rgb_R11;//接到板子上面的PWM口11 R int rgb_G9;//接到板子上面的PWM口9 G int rgb_B10;//接到板子上面的PWM口10 B void setup() {pinMode(rgb_R,OUTPUT);//设置rgb_R的控制口为输出模式pinMode(rgb_G,OUTPUT);//设置rgb_G的控制口为输出模…

多线程【进阶版】

目录 一. 常见的锁策略 1.1 乐观锁和悲观锁 1.2 轻量级锁和重量级锁 1.3 自旋锁和挂起等待锁 1.4 互斥锁和读写锁 1.5 可重入锁和不可重入锁 1.6 公平锁和非公平锁 1.7 关于锁策略的相关面试题 二. CAS 三. Synchronized 原理 3.1 基本特点 3.2 加锁步骤 3.3 锁消除…

利用宏简化Q_PROPERTY动态属性的定义

目录 写在前面实现历程传统定义方式预想的方式(事实上有一点点区别) 例程mainwindow.hmainwindow.cppmain.cpp 执行结果如上事实的使用方法 写在前面 上一篇写了pyqt如何更加便利地定义动态属性&#xff0c;关于C版的其实在我刚接触Qt不久就想过并做了一些尝试&#xff0c;但死…

C. Vus the Cossack and Strings(异或判断二进制位匹配数奇偶)

Problem - C - Codeforces 题目描述 Vus the Cossack has two binary strings, that is, strings that consist only of "0" and "1". We call these strings aa and bb . It is known that |b| \leq |a|∣b∣≤∣a∣ , that is, the length of bb is at m…

手把手教你学习IEC104协议和编程实现 十二-读取定值

不废话,直接接上一章开始。忘记的复习一下上一章。 读取多个/全部定值 结构 设计思路 先说一下,读取多个定值的思路,首先。。。我靠,好长时间不看这一部分的程序,忘记了,待我先好好捋一捋。。。 言归正传,首先我们要选择我们要调用的定值。如下图: 大概就这样举个例…

2023年数据治理工程师证书有哪些值得考?DAMA-CDGA/CDGP

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…