SpringCloud---Gateway

(一).网关介绍

1.前置问题

当前,我们的生产是介绍openfeign的环境。

上图分别是product-service和order-service中的方法。这就有问题,当前所有微服务的接口都是直接对外暴露的,可以直接通过外部访问。为了保证对外服务的安全性,服务端实现的微服务接口通常都带有一定的权限校验机制。由于使用了微服务,原本一个应用的多个模块拆分成了多个应用,我们不得不实现多次校验逻辑。当这套逻辑需要修改时,我们需要修改多个应用,所以会很麻烦。

针对于上述问题,一个常用的解决方案就是使用API网关

2.什么是API网关

API网关(简称网关)也是一个服务,通常是后端服务的唯一入口。它的定义类似设计模式中的门面模式。它就类似整个微服务架构的门面,所有的外部客户端访问,都需要经过它来进行调度和过滤。

网关的核心功能

(1).权限控制:作为微服务的入口,对用户进行权限校验,如果校验失败则进行拦截

(2).动态路由:一切请求先经过网关,但是网关不处理业务,而是根据狗中规则,把请求转发到某个微服务

(3).负载均衡:当路由的目标服务有多个时,还需要做负载均衡

**(4).限流:**请求两两过高时,按照网管中配置微服务能够接收的流量进行放行,避免服务压力过大

3.常见网关的实现

(1).Zuul

Zuul是Netflix公司开源的一个API网关组件,是Spring Cloud Netflix子项目的核心组件之一,它可以和Eureka,Ribbon,Hystrix等组件配合使用

(2).Spring Cloud Gateway

Spring Cloud Gateway 是 Spring Cloud 的一个全新的API网关项目,基于Spring + SpringBoot等技术开发,目的是为了替换掉Zuul。

(二).Spring Cloud Gateway

1.使用Gateway

(1).创建网关项目

API网关也是一个服务,所以需要创建一个网关项目

(2).引入网关相关依赖

XML 复制代码
    <dependencies>
        <!--⽹关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!--基于nacos实现服务发现依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
         </dependency>

         <!--负载均衡-->
         <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-loadbalancer</artifactId>
         </dependency>
        
    </dependencies>

(3).写启动类

(4).配置文件

在application.yml文件中,添加配置

XML 复制代码
server:
  port: 5050
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr:       #服务器nacos地址
        namespace: 68576334-aeee-446f-a704-c59fd9207ecf    #设置命名空间   服务管理的命名空间,并不是配置管理的命名空间
    gateway:
      routes:     #网关路由相关配置
        - id: product-service     #路由规则id,随便起,不重复即可
          uri: lb://product-service/**    #目标服务地址,lb表示"负载均衡"
          predicates:     #路由条件,当满足条件的时候,网关应用会把请求跳转到目标服务地址
            - Path=/product/**        #把符合Path规则的一切请求,都代理到uri参数指定的地址


        - id: order-service
          uri: lb://order-service/**
          predicates:
            - Path=/order/**

(5).运行测试

可以发现,访问的时候,使用的是gateway的端口号,是可以访问成功的

2.Route Predicate Factories

(1).Predicate

Ⅰ.概念

Pridicate是Java 8 提供的一个函数式编程接口,它接收一个参数并返回一个布尔值,用于条件过滤,请求参数的校验

下面通过一个例子来看

①.定义一个Predicate

②.使用这个Predicate

Ⅱ.Predicate的其他写法

A.内置函数(内部类)

B.lambda写法

Ⅲ.Predicate的其他方法

|----------------------------------------------------------|-----------------------------------------------------------|
| 方法 | 说明 |
| boolean test (T t) | 判断条件,可以理解为 条件A 根据逻辑返回布尔值 |
| Predicate<T> and (Predicate <? super T> other) | 条件A && 条件B 当前Predicate的test && other的test方法,相当于进行两次判断 |
| default Predicate<T> negate() | !条件A 对当前判断进行 "!"操作,即取非操作 |
| default Predicate<?> or(Predicate <? super T> other) | 条件A || 条件B 当前Predicate的test方法 || other 的test方法 |

下面进行具体的演示

(2).Route Predicate Factories

Route Predicate Factories,路由断言工厂,也称为路由谓词(函数)工厂,在Spring Cloud Gateway中,Predicate提供了路由规则的匹配机制

我们在配置文件中写的断言规则只是字符串,这些字符串会被Route Predicate Factory读取并操作,转变为路由判断条件。

例如,前面写的Path,就是通过Path属性来匹配URL前缀是 /product的请求

这个规则是由 PathRoutePredicateFactory 这个类实现的

Spring Cloud Gateway 默认提供了很多 Route Predicate Factory,这些Predicate 会分别匹配HTTP请求的不同属性,并且多个Predicate可以通过and逻辑进行组合

|------------|--------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
| 名称 | 说明 | 示例 |
| After | 这个工厂需要一个日期时间(ZonedFateTime对象), 匹配指定日期之后的请求 | predicates: - After=2017-01-20T17:42:47.789-07:00America/Denver |
| Before | 匹配指定日期之前的请求 | predicates: - Before=2017-01-20T17:42:47.789-07:00America/Denver |
| Between | 匹配两个指定时间之间的请求,datetime2的参数必须在datetime1之后 | predicates: - Between=2017-01-20T17:42:47.789-07:00America/Denver , 2017-02-20T17:42:47.789-07:00America/Denver |
| Cookie | 请求中包含指定Cookie,且该Cookie值符合指定的正则表达式 | predicates: - Cookie=chocolate , ch.p |
| Header | 请求中包含指定Header,且该Header值符合指定的正则表达式 | predicates: - Header=X-Request-id , \d+ |
| Host | 请求必须是访问某个host(根据请求中的Host字段进行匹配) | predicates: - Host=**.somehost.org,**.anotherhost.or g |
| Method | 匹配指定的请求方式 | predicates: - Method=GET,POST |
| Path | 匹配指定规则的路径 | predicates: - Path=/order/** |
| RemoteAddr | 请求者的IP必须为指定范围 | predicates: - RemoteAddr=192.168.1.1*24 |

下面通过一个示例来演示

可以看到,是可以访问成功的

可以看到,是访问不到的

3.Gateway Filter Factories

Gateway Filter Factories,网关过滤工厂。Predicate决定了请求由哪一个路由处理,如果在请求前后需要加一些逻辑,这个就是Filter(过滤器)的作用范围了。

Filter 分为两种类型:Pre类型和Post类型

Pre类型过滤器:路由处理之前执行(请求转发到后端服务之前执行),在Pre类型过滤器中可以做鉴权,限流等等。

Post类型过滤器:请求执行完成后,将结果返回给客户端之前执行

Spring Cloud Gateway中内置了很多Filter,用于拦截和链式处理web请求,比如权限校验,访问超时等设定。

Spring Cloud Gateway从作用范围上,把Filter分为GatewayFilterGlobalFilter

GatewayFilter:应用到单个路由或者一个分组的路由上

GlobalFilter:应用到所有的路由上,也就是对所有的请求生效

(1).GatewayFilter

A.概念

GatewayFilter和Predicate类型,都是在配置文件application.yml中配置,每个过滤器的逻辑都是固定的。

例如,AddRequestParameterGatewayFilterFactory 只需要在配置文件中写 AddRequestParameter,就可以为所有的请求添加一个参数。

B.使用

下面,通过一个例子来看

Ⅰ.在application.yml中添加filter

这个filter只添加在了order-service路由下,因此支队order-service路由生效,也就是对 /product/**的请求生效。

Ⅱ.接收参数并打印

在order-service服务中接收请求的参数,并打印出来

C.GatewayFilter说明

Spring Cloud Gateway提供的Filter非常多,下面列出一些常见过滤器的说明

①.AddRequestHeader,为当前请求添加Header

  • AddRequestHeader=X-Request-red, blue
    参数: Header的名称及值
    ②.AddRequestParam,为当前请求添加请求参数
  • AddRequestParameter=red, blue
    参数: 参数的名称及值
    ③.AddResponseHeader,为响应结果添加Header
  • AddResponseHeader=X-Response-Red, Blue
    参数: Header的名称及值
    ④.RemoveRequestHeader,从当前请求删除某个Header
  • RemoveRequestHeader=X-Request-Foo
    参数: Header的名称
    ⑤.RemoveResponseHeader,从响应结果中删除某个Header
  • RemoveResponseHeader=X-Response-Foo
    参数: Header的名称
    ⑥.RequestRateLimiter,为当前网关的所有请求执行限流过滤,如果被限流,默认会相应HTTP -429-TooManyRequests。默认提供了RedisRateLimiter的限流实现,采用令牌桶算法实现限流功能。
    filters:
  • name: RequestRateLimiter
    args:
    redis-rate-limiter.replenishRate: 10
    redis-rate-limiter.burstCapacity: 20
    redis-rate-limiter.requestedTokens: 1

redis-rate-limiter.replenishRate指的是令牌填充速度,即每秒周允许多少个请求(不丢弃任何请求)

redis-rate-limiter.burstCapacity指的是令牌桶容量,即每秒钟用户最大能够执行的请求数量(不丢弃任何请求),如果将此值设置为0,则将会阻止所有请求。

redis-rate-limiter.requestedTokens指的是每次请求占用几个令牌,默认为1

关于限流,使用的限流算法进行的实现,具体有四种限流算法

假设,现在的流量限制为1000次/分钟

a.固定窗口

但是,这种算法也有明显的缺点

如果说在59秒~60秒之间突然来了1000个请求,然后60~61秒之间突然又来了1000个请求,此时相当于2秒钟来了2000个请求,但是流量限制为1000次/分钟,此时就可能会把服务器搞挂掉

b.滑动窗口

可以看到,滑动窗口可以解决"固定窗口"存在的问题。但是滑动窗口并不能从根本上解决,如果使用滑动窗口,那么我们需要设置滑动窗口滑动的间隔

c.漏桶算法

但是,这种算法,也会出现"固定窗口"的问题,如果一下子来了1000个请求,此时全部会被放在桶里,但是消费者还是按照固定的速率进行处理

d.令牌桶算法

现在,如果在来请求之前,桶里已经放了1000个令牌,那么当这1000个请求突然来的时候,每个请求都拿着一个令牌,然后处理,不会出现应激流量处理不好的问题
⑦.Retry,针对不同的响应进行重试,当后端服务不可用时,网关会根据配置参数来发起重试请求
filters:

  • name: Retry
    args:
    retries: 3
    statuses: BAD_REQUEST

retries:重试次数,默认为3

status:HTTP请求返回的状态码,针对指定状态码进行重试,对应org.springframework.http.HttpStatus


⑧.RequestSize,设置允许接收最大请求包的大小,如果请求包大小超过设置的值,则返回413 Payload TooLarge。请求包大小,单位为字节,默认为5M
filters:

  • name: RequestSize
    args:
    maxSize: 5000000

⑨.默认过滤器

添加一个fliter并将其应用于所有路由,这个属性需要一个filter的列表。

可以看到,在上面的图片中,我就是将gateway中配置了默认过滤器,然后当状态为BAD_REQUEST,对应的状态码为400,的时候,就要重试3次。然后我在getDetail()方法中,将状态码改成了400,然后运行程序,进行访问,发现,确实重试了3次。

(2).GlobalFilter

A.概念

GlobalFilter是Spring Cloud Gateway中的全局过滤器,它和GatewayFilter的作用域是相同的。GlobalFilter会应用到所有的路由请求上,全局过滤器通常用于实现与安全性,性能监控和日志操作等相关的全局功能。

Spring Cloud Gateway内置的全局过滤器也有很多,例如

Gateway Metrics Filter:网关指标,提供监控指标

Forward Routing Filter:用于本地forward,请求不转发到下游服务器

LoadBalancer Client Filter:针对下游服务,实现负载均衡

B.使用

①.添加依赖

XML 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

②.添加配置

③.运行测试

可以看到,所有的监控信息链接都显示出来了

4.过滤器执行顺序

如果一个项目中,既有GatewayFilter,又有GlobalFilter,那么当请求路由后,网关会把当前项目中的GatewayFilter和GlobalFilter合并到一个过滤器链(集合)中,并进行排序,依次执行过滤器

对于每一个过滤器,都必须要指定一个int类型的order值,默认为0,表示该过滤的优先级,order值越小,优先级越高,执行顺序越靠前

Filter通过实现Order接口或者添加@Order注解来指定order值

Spring Cloud Gateway提供的Filter由Spring指定,用户也可以自定义Filter,由用户指定

当过滤器的order值一样的时候,会按照 defaultFilter > GatewayFilter > GlobalFilter

5.自定义过滤器

(1).自定义GatewayFilter

自定义GatewayFilter,需要去实现对应的接口GatewayFilterFactory,SpringBoot默认帮我们实现的抽象类是AbstractGatewayFilterFactory,我们可以直接使用

Ⅰ.定义GatewayFilter
java 复制代码
package org.com.spring;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Slf4j
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {

    
    public CustomGatewayFilterFactory() {
        super(CustomConfig.class);
    }

    @Override
    public GatewayFilter apply(CustomConfig config) {
        return new GatewayFilter() {

            /**
             *  ServerWebExchange           HTTP请求-响应交互契约,提供了对HTTP 请求 和 响应 的比例
             *
             *  GatewayFilterChain          过滤器链
             *
             *  Mono                        Reactor的核心类,数据流发布者,Mono最多只能触发一个事件,可以把Mono用在异步完成任务时,发出通知
             *
             *  chain.filter(exchange)      执行请求
             *
             *  Mono.fromRunnable()         创建一个包含Runnable元素的数据流
             *
             * @return
             */

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

                //执行步骤 : Pre类型   ->  执行请求   ->  Post请求

                log.info("Pre Filter ,config : {}",config);

                return chain.filter(exchange).then(Mono.fromRunnable(()->{
                    log.info("Post  Filter....");
                }));
            }
        };
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;    //设置较低的优先级
    }
}

针对这个Filter的配置,使用CustomConfig定义

java 复制代码
package org.com.spring;

import lombok.Data;

@Data
public class CustomConfig {

    //和yml的配置对应
    private String name;
}

注意:

①.类名统一以GatewayFilterFactory结尾 ,因为在默认情况下,过滤器的name会采用该定义类的前缀,这里的name=Custom(yml配置中使用)

②.apply方法中,同时包含了Pre和Post过滤,then方法中是请求执行结束之后处理的

③.CustomConfig是一个配置类,该类只有一个属性name,和yml的配置对应

④.该类需要交给Spring管理,所以需要添加@Component

⑤.getOrder表示该过滤器的优先级,值越大,优先级越低

Ⅱ.配置过滤器
Ⅲ.运行测试

可以看到,自定义的GatewayFilter起作用了

(2).自定义GlobalFilter

GlobalFilter的实现比较简单,不需要额外的配置,只需要实现GlobalFilter接口,自动会过滤所有的Filter

java 复制代码
package org.com.spring;

import lombok.extern.slf4j.Slf4j;
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;

@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("Pre Global Filter");
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            log.info("Post Global Filter");

        }));
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

可以看到,当GatewayFilter和GlobalFilter过滤器order一样时,会先执行GatewayFilter

相关推荐
我有满天星辰1 小时前
Spring AI Prompt 实战:System Prompt、User Prompt 与 PromptTemplate
人工智能·spring·prompt
剑胆琴心静水深流9 小时前
全栈之路6---web集成与呈现
前端·vue.js·spring boot·分布式·spring·前端框架·npm
晓晓_za89866812 小时前
Geo 优化服务 CI/CD 流水线搭建:源码自动构建、测试与灰度发布
运维·服务器·tcp/ip·spring·缓存·ci/cd
卓怡学长13 小时前
w180springboot基于Java的悠扬乐器管理
java·spring boot·mysql·spring·maven·intellij-idea
小白说大模型16 小时前
Spring AI 框架中集成 MCP 的完整指南:从服务端到客户端的全流程实践
大数据·数据库·人工智能·安全·spring·chatgpt·开源
卓怡学长17 小时前
w181springboot机场乘客服务系统
java·数据库·spring boot·spring·intellij-idea
卓怡学长21 小时前
w192基于springboot教务管理系统
java·数据库·spring boot·spring·maven·intellij-idea
手握风云-1 天前
Spring Cloud:分布式系统的“粘合剂”(六)
后端·spring·spring cloud
AI_小站1 天前
刚面完百度的 Agent 开发岗,我才发现:世界就是个巨大的草台班子
java·开发语言·人工智能·spring·百度·langchain