【微服务-网关】SpringCloud GateWay核心技术

在前面的文章中我们介绍了微服务网关的基础知识,了解了什么是网关,网关有什么作用,以及市面上有哪些成熟的网关产品,最后了解了网关的配置技巧。通过上篇文章,大家应该可以在微服务架构中完成网关的基本配置。 但是,文末最后也说了,自动路由可以解决简单的转发规则,但对于企业中遇到的复杂、特殊的路由转发规则,就不是自动路由能解决的了。SpringCloud GateWay项目中内置了强大的“谓词”系统,可以满足企业应用中的各种转发规则要求,下面我们就一起来看看吧!

一、谓词(Predicate)

在介绍前,我们先要了解一下网关的三个关键名词:路由(Route)、谓词(Predicate)、过滤器(Filter)。

路由(Route)是指一个完整的网关地址映射与处理过程。一个完整的路由包含两部分配置:谓词(Predicate)与过滤器(Filter)。前端应用发来的请求要被转发到哪个微服务上,是由谓词决定的;而转发过程中请求、响应数据被网关如何加工处理是由过滤器决定的。

我们通过实例来介绍,将上篇文章中的工程复制过来,修改一下application.yml文件:

spring:

  application:

    name: gateway

  cloud:

    nacos:

      discovery:

        server-addr: 106.14.221.171:8848

        username: nacos

        password: nacos

    gateway: 

      discovery:

        locator:

          enabled: false #不再需要Gateway路由转发

      routes:  #路由规则配置

        #第一个路由配置,service-a路由规则

        - id: service_a_route #路由唯一标识

          #lb开头代表基于gateway的负载均衡策略选择实例

          uri: lb://service-a 

          #谓词配置

          predicates:

            #Path路径谓词,代表用户端URI如果以/a开头便会转发到service-a实例

            - Path=/a/** 

            #After生效时间谓词,2024年5月1日后该路由才能在网关对外暴露

            - After=2024-15-01T00:00:00.000+08:00[Asia/Shanghai]

          #谓词配置

          filters:

            #忽略掉第一层前缀进行转发

          - StripPrefix=1 

            #为响应头附加X-Response=Blue

          - AddResponseHeader=X-Response,Blue 

        #第二个路由配置,service-b路由规则

        - id: service_b_route

          uri: lb://service-b

          predicates:

            - Path=/b/**

          filters:

            - StripPrefix=1

server:

  port: 80

management:

  endpoints:

    web:

      exposure:

        include: '*'

配置释义:在 2024 年 5 月 1 日后,当用户端发来/a/...开头的请求时,Spring Cloud Gateway 会自动获取 service-a 可用实例,默认采用轮询方式将URI附加至实例地址后,形成新地址,service-a处理后 Gateway 网关自动在响应头附加 X-Response=Blue。至于第二个 service_b_route,比较简单,只说明当用户访问/b开头 URL 时,转发到 service-b 可用实例。

这里提供一个完整的路由配置固定格式:

spring:

    gateway: 

      discovery:

        locator:

          enabled: false  #不再需要Gateway路由转发

      routes: 

        - id: xxx #路由规则id

          uri: lb://微服务id  #路由转发至哪个微服务

          predicates: 

         //具体的谓词

         filters:

         //具体的过滤器

其中 predicates 是重点,说明路由生效条件,这里我们将常见的谓词使用形式举例出来:

1、After

After代表在指定时间点后路由规则生效

predicates:

    - After=2024-05-01T00:00:00.000+08:00

2、Before

Before代表在指定时间点前路由规则生效

predicates:

    - Before=2024-05-01T17:42:47.789-07:00[America/Denver]

3、Path

Path 代表 URI 符合映射规则时生效

predicates:

    - Path=/b/**

4、Header

Header 代表包含指定请求头时生效

predicates:

    - Header=X-Request-Id, \d+

5、Method

Method 代表要求 HTTP 方法符合规定时生效

predicates:

    - Method=GET

谓词是 Gateway 网关中最灵活的部分,上面列举的是最常用的谓词,还有很多谓词是在文中没有提到,有兴趣可以到官网中学习。

二、过滤器(Filter)

过滤器(Filter)可以对请求或响应的数据进行额外处理,下面我们也来列举几个常用的过滤器

1、AddRequestParameter

AddRequestParameter 是对所有匹配的请求添加一个查询参数

filters:

- AddRequestParameter=id,2 #在请求参数中追加id=2

2、AddResponseHeader

AddResponseHeader 会对所有匹配的请求,在返回结果给客户端之前,在 Header 中添加响应的数据

#在Response中添加Header头,key=token,Value=123。

filters:

- AddResponseHeader=token,123

3、Retry

Retry 为重试过滤器,当后端服务不可用时,网关会根据配置参数来发起重试请求

filters:

#涉及过滤器参数时,采用name-args的完整写法

- name: Retry #name是内置的过滤器名

  args: #参数部分使用args说明

    retries: 3

    status: 503
# 含义为,当后端服务返回 503 状态码的响应后,Retry 过滤器会重新发起请求,最多重试 3 次

三、GateWay的执行原理

在了解了SpringCloud GateWay配置和谓词用法后,我们再来看一下GateWay的底层实现。

执行流程图如下:

  • 1、Spring Cloud Gateway 启动时基于 Netty Server 监听指定的端口(该端口可以通过 server.port 属性自定义)。当前端应用发送一个请求到网关时,进入 Gateway Handler Mapping 处理过程,网关会根据当前 Gateway 所配置的谓词(Predicate)来决定是由哪个微服务进行处理。

  • 2、确定微服务后,请求向后进入 Gateway Web Handler 处理过程,该过程中 Gateway 根据过滤器(Filters)配置,将请求按前后顺序依次交给 Filter 过滤链进行前置(Pre)处理,前置处理通常是对请求进行前置检查,例如:判断是否包含某个指定请求头、检查请求的 IP 来源是否合法、请求包含的参数是否正确等。

  • 3、当过滤链前置(Pre)处理完毕后,请求会被 Gateway 转发到真正的微服务实例进行处理,微服务处理后会返回响应数据,这些响应数据会按原路径返回被 Gateway 配置的过滤链进行后置处理(Post),后置处理通常是对响应进行额外处理,例如:将处理过程写入日志、为响应附加额外的响应头或者流量监控等。

可以看到,在整个处理过程中谓词(Predicate)与过滤器(Filter)起到了重要作用,谓词决定了路径的匹配规则,让 Gateway 确定应用哪个微服务,而 Filter 则是对请求或响应作出实质的前置、后置处理。

在项目中功能场景多种多样,像日常的用户身份鉴权、日志记录、黑白名单、反爬虫等基础功能都可以通过自定义 Filter 为 Gateway 进行功能扩展。

四、自定义全局过滤器实战

在 Spring Cloud Gateway 中,自定义过滤器分为两种:全局过滤器局部过滤器。两者唯一的区别是:全局过滤器默认应用在所有路由(Route)上,而局部过滤器可以为指定的路由绑定。下面通过“计时过滤器”这个案例介绍全局过滤器的配置。所谓计时过滤器是指任何从网关访问的请求,都要在日志中记录下从请求进入到响应退出的执行时间,通过这个时间运维人员便可以收集并分析哪些功能进行了慢处理,以此为依据进行进一步优化。下面是计时过滤器的代码:

package com.example.gateway.filter;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;

import org.springframework.cloud.gateway.filter.GlobalFilter;

import org.springframework.core.Ordered;

import org.springframework.stereotype.Component;

import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

@Component //自动实例化并被Spring IOC容器管理

//全局过滤器必须实现两个接口:GlobalFilter、Ordered

//GlobalFilter是全局过滤器接口,实现类要实现filter()方法进行功能扩展

//Ordered接口用于排序,通过实现getOrder()方法返回整数代表执行当前过滤器的前后顺序

public class ElapsedFilter implements GlobalFilterOrdered {

    //基于slf4j.Logger实现日志输出

    private static final Logger logger = LoggerFactory.getLogger(ElapsedFilter.class);

    //起始时间属性名

    private static final String ELAPSED_TIME_BEGIN = "elapsedTimeBegin";

    /**

     * 实现filter()方法记录处理时间

     * @param exchange 用于获取与当前请求、响应相关的数据,以及设置过滤器间传递的上下文数据

     * @param chain Gateway过滤器链对象

     * @return Mono对应一个异步任务,因为Gateway是基于Netty Server异步处理的,Mono对就代表异步处理完毕的情况。

     */


    @Override

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

        //Pre前置处理部分

        //在请求到达时,往ServerWebExchange上下文环境中放入了一个属性elapsedTimeBegin,保存请求执行前的时间戳

        exchange.getAttributes().put(ELAPSED_TIME_BEGIN, System.currentTimeMillis());



        //chain.filter(exchange).then()对应Post后置处理部分

        //当响应产生后,记录结束与elapsedTimeBegin起始时间比对,获取RESTful API的实际执行时间

        return chain.filter(exchange).then(

                Mono.fromRunnable(() -> { //当前过滤器得到响应时,计算并打印时间

                    Long startTime = exchange.getAttribute(ELAPSED_TIME_BEGIN);

                    if (startTime != null) {

                        logger.info(exchange.getRequest().getRemoteAddress() //远程访问的用户地址

                                + " | " +  exchange.getRequest().getPath()  //Gateway URI

                                + " | cost " + (System.currentTimeMillis() - startTime) + "ms"); //处理时间

                    }

                })

        );

    }

    //设置为最高优先级,最先执行ElapsedFilter过滤器

    //return Ordered.LOWEST_PRECEDENCE; 代表设置为最低优先级

    @Override

    public int getOrder() {

        return Ordered.HIGHEST_PRECEDENCE;

    }

}

运行后通过 Gateway 访问任意微服务便会输出日志:

2024-03-1 12:36:01.765  INFO 14052 --- [ctor-http-nio-4] com.example.gateway.filter.ElapsedFilter   : /0:0:0:0:0:0:0:1:57873 | /test-service/test | cost 821ms

以上就是全局过滤器的开发方法,至于局部过滤器的配置方法与全局过滤器极为相似,有兴趣可以通过官方文档了解更详细的内容。

下一篇开始,我们来介绍一下微服务环境下如何通过服务降级、熔断等机制来保护我们的微服务架构,避免雪崩效应的发生。感兴趣的小伙伴可以持续关注。

文章将持续更新,欢迎关注公众号:服务端技术精选。欢迎点赞、关注、转发

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

君正X2100 RTOS JPEG硬件编码

一、配置 进入SDK的tools/iconfigtool/IConfigToolApp目录&#xff0c;执行./IConfigTool指令&#xff0c;进入配置界面&#xff1a; Config.in 是生成配置界面的文件&#xff0c;Config是需要修改的配置文件&#xff0c;选择之后点击Open。 选择 xburst2系列CPU->X2000系列…

Matter - nordic OTA(3)

Nordic Semiconductor 开发平台示例下&#xff0c;提供了两套进行空中 (OTA) 设备固件升级 (DFU)的方式&#xff1a; 1.基于 Matter OTA 更新协议规范下使用 Matter 网络查询和下载新的固件映像&#xff0c;需要 OTBR。 2. nordic 基于低功耗蓝牙(Bluetooth LE)实现的简单管理协…

golang学习随便记15

golang测试 go test 命令 go test 命令是一个按照一定的约定和组织来测试代码的程序。我们需要了解有哪些约定和组织&#xff1a;在包目录内&#xff0c;所有后缀为 _test.go 的源文件不会被 go build 构建命令构建成包的一部分&#xff0c;相反&#xff0c;它们会被 go test…

java常用IO流功能——字符流和缓冲流概述

前言&#xff1a; 整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup! 之前说了下了IO流的概念&#xff0c;并整理了字节流&#xff0c;有需要的可以看这篇 java常用应用程序编程接口&#xff08;API&#xff09;——IO流概述及字节流的使用 字符流 FileReader(文件字…

鸿蒙TypeScript入门学习第一天【简单介绍】

1.TypeScript 教程 TypeScript 是 JavaScript 的一个超集&#xff0c;支持 ECMAScript 6 标准&#xff08;&#xff09;。 TypeScript 由微软开发的自由和开源的编程语言。 TypeScript 设计目标是开发大型应用&#xff0c;它可以编译成纯 JavaScript&#xff0c;编译出来的 …

BIM插件定制,你的功能由你说了算!python开发 | 图形引擎 | 几何引擎

各位CSDN的宝宝们~ 如果你对插件开发或BIM技术兴趣十足 有着无限的想法和创意 想解决日常使用BIM软件的难题&#xff0c;打破桎梏 却愁于没有实现的机会 现在&#xff0c;机会来了&#xff01; BIMBase插件命题征集活动已经开启&#xff01; &#xff08;没错就是征集&am…

AI时代-普通人的AI绘画工具对比(Midjouney与Stable Diffusion)

AI时代-普通人的AI绘画工具对比&#xff08;Midjouney与Stable Diffusion&#xff09; 前言1、基础对比Stable Diffusion&#xff08;SD&#xff09;SD界面安装与使用SD Midjouney&#xff08;MJ&#xff09; 2、硬件与运行要求对比Stable Diffusion硬件要求内存硬盘显卡 Midjo…

647. 回文子串

#动态规划法 class Solution:def countSubstrings(self, s: str) -> int:n len(s)#dp[i][j] [i,j]是否为回文串dp [[False]*n for _ in range(n)]res 0#dp[i][j]依赖于dp[i1][j-1]&#xff0c;所以i要从下往上遍历for i in range(n-1,-1,-1):for j in range(i,n):if s[i]…

基于java+springboot+vue实现的超市管理系统(文末源码+Lw+ppt)23-354

摘 要 系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方法对超市管理的现状进行系统调查。采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自下而上”…

神经网络代码实现(用手写数字识别数据集实验)

目录 一、前言 二、神经网络架构 三、算法实现 1、导入包 2、实现类 3、训练函数 4、权重参数矩阵初始化 5、参数矩阵变换向量 6、向量变换权重参数矩阵 7、进行梯度下降 7.1、损失函数 7.1.1、前向传播 7.2、反向传播 8、预测函数 四、完整代码 五、手写数字识别 一、前言 …

创建数组的时候,数组大小是确定数值和变量的不同情况

概要&#xff1a; 1、创将数组的时候&#xff0c;如果数组大小是确定数值 &#xff08;1&#xff09;数组所有元素默认是0 &#xff08;2&#xff09;可以通过大括号对元素进行赋值 int arr[3]{1,2,3}; int arr[10]{1}; //只将第一个元素赋值为1,其他元素依然是0 2、…

istio 设置 istio-proxy sidecar 的 resource 的 limit 和 request

方式一 修改 configmap 查看当前 sidecar 的 cpu 和 memory 的配额 在 istio-sidecar-injector 中查找,修改后重启 pod 可以生效(下面那个 proxy_init 配置不管,不知道是干嘛的) 方式二 如果是通过 iop 安装的 istio,可以修改 iop 文件中的配置 spec:values:global:…

静态住宅IP VS 动态住宅IP,怎么选择?

在进行海外 IP 代理时&#xff0c;了解动态住宅 IP 和静态住宅 IP 的区别以及如何选择合适的类型非常重要。本文将介绍精态住宅 IP 特点和&#xff0c;并提供选择建议&#xff0c;帮助您根据需求做出明智的决策。 静态住宅 IP 的特点 静态住宅 IP 是指 IP 地址在一段时间内保…

【Java程序设计】【C00360】基于Springboot的考研互助交流平台(有论文)

基于Springboot的考研互助交流平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 项目获取 &#x1f345;文末点击卡片获取源码&#x1f345; 开发环境 运行环境&#xff1a;推荐jdk1.8&#xff1b; 开发工具&#xff1a;eclipse以及i…

Cadence——生成Gerber制板文件

软件版本&#xff1a;Cadence SPB OrCAD Allegro 16.6 打开Allegro PCB Designer 选择如下选项&#x1f447; 点击 File–>Open&#xff0c;找到对应的.brd文件 电气错误的检查&#xff1a;点击 Display–>Status&#xff0c;全为绿色则没有错误 按照不同的项目来进…

【SQL】1517. 查找拥有有效邮箱的用户(正则表达式regexp)

前述 sql-正则表达式SQL学习笔记 – REGEXP 题目描述 leetcode 题目&#xff1a;1517. 查找拥有有效邮箱的用户 Code select * from Users where mail regexp ^[a-zA-Z][a-zA-Z0-9_.-]*leetcode\\.com$图片引用自 MySQL正则表达式

C++中的内存分区

栈&#xff1a;在执行函数时&#xff0c;函数内局部变量的存储单元都可以在栈上创建&#xff0c;函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中&#xff0c;效率很高&#xff0c;但是分配的内存容量有限 堆&#xff1a;就是那些由 new分配的内…

【Python版】手把手带你如何进行Mock测试

什么是mock&#xff1f; mock测试是以可控的方式模拟真实的对象行为。程序员通常创造模拟对象来测试对象本身该具备的行为&#xff0c;很类似汽车设计者使用碰撞测试假人来模拟车辆碰撞中人的动态行为 为什么要使用Mock&#xff1f; 之所以使用mock测试&#xff0c;是因为真…

matplotlib查询当前系统所有字体

电脑里有这个字体但是不代表matplotlib里也有这个字体&#xff0c;所以解决matplotlib中的中文显示问题主要就是要找到它所内置支持的字体&#xff0c;那么我们首先查看一下它的内置字体&#xff0c;运行以下代码查看所支持的字体 # 查询当前系统所有字体 from matplotlib.fon…