自学内容网 自学内容网

Spring Cloud Gateway

Spring Cloud Gateway

1. 问题

前面我们的程序所有微服务的接口都是直接对外暴露的.可以直接对外访问.

但是为了保证对外服务的安全性,服务端实现的微服务接口通常带有一定的权限校验机制.

由于使用了微服务,原本一个应用的多个模块拆分成了多个应用,导致我们不得不实现多个校验逻辑.

此时带来了开发以及维护的麻烦

一个常用的解决方法就是使用API网关

2. 什么是API网关

API网关也是一个服务,通常是后端服务的唯一入口. 它的定义类似于设计模式中的Facade模式(门面模式).

也就是说,API网关就是整个微服务架构的门面,所有外部客户端访问,都需要经过它来进行调度和过滤

网关的核心功能

  1. 权限控制: 作为微服务的入口,对用户进行权限校验,如果拦截失败则进行拦截
  2. 动态路由: 一切请求先经过网关,但网关不处理业务,只是通过某种规则,把请求转发到某个微服务
  3. 负载均衡:当路由的目标有多个的时候,需要负载均衡
  4. 限流: 请求流量过大时,按照网关中配置微服务的能够接受的流量进行放行,避免服务压力过大

3. 常见的网关实现

Zuul

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

但是现在Zuul等组件进入了维护状态,不再进行新特性的研发

Spring Cloud Gateway

Spring Cloud Gateway是Spring Cloud 的一个全新的api网关项目,基于Spring + Spring Boot等技术开发,替换掉了Zuul,旨在为微服务架构提供一种全新而有效的途径来转发请求,并为他们提供了横切关注点,如安全性,监控和弹性

在性能方面经过测试,优于Zuul

4. Spring Cloud Gateway快速上手

创建网关模块

引入依赖

<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>

编写启动类

@SpringBootApplication
public class GateWayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GateWayApplication.class,args);
    }
}

添加网关的路由配置

server:
  port: 10030
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes: # 网关路由配置
        - id: product-service # 这里是自定义的 只要求不重复即可
          uri: lb://product-service
          predicates:
            - Path=/product/**
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
  1. id: 自定义路由ID.保持唯一
  2. uri: 目标服务地址,支持普通URI以及 lb://应用注册服务名称,lb代表负载均衡,使用lb://方式表示从注册中心获取服务地址
  3. predicates:路由条件,根据匹配的结果决定是否要执行该路由请求

测试

5. Route Predicate Factories

5.1 Predicate

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

  1. 定义一个Predicate
public class StringPredicate implements Predicate<String> {

    @Override
    public boolean test(String s) {
        return s == null || s.isEmpty();
    }
}
  1. 使用
public class TestString {
    @Test
    public void t1 () {
        Predicate<String> predicate = new StringPredicate();
        String s1 = "";
        String s2 = "11";
        System.out.println(predicate.test(s1));
        System.out.println(predicate.test(s2));
    }
}
  1. 执行结果

也可以使用内置函数或者Lambda

@Test
public void t1 () {
    Predicate<String> predicate = s -> s != null && !s.isEmpty();
    System.out.println(predicate.test("abc"));
}

Predicate的其他方法


// and
@Test
public void t2 () {
    Predicate<String> predicate = s -> s != null && !s.isEmpty();
    Predicate<String> predicate1 = s -> s.chars().allMatch(Character::isDigit);
    String s1 = "1111";
    String s2 = "asddd";
    System.out.println(predicate.and(predicate1).test(s1));
    System.out.println(predicate.and(predicate1).test(s2));
}

// negate
@Test
public void t3 () {
    Predicate<String> predicate = s -> s != null && !s.isEmpty();
    String s = "11";
    System.out.println(predicate.negate().test(s));
}

// or
@Test
public void t4 () {
    Predicate<String> predicate = s -> s != null && !s.isEmpty();
    Predicate<String> predicate1 = s -> s.chars().allMatch(Character::isDigit);
    String s = "aaaa";
    System.out.println(predicate.or(predicate1).test(s));
}

5.2 Route Predicate Factories

Route Predicate Factories (路由断言工厂),在Spring Cloud GateWay中,Predicate提供了路由规则的匹配机制

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

例如这个Path=/product/**,就是通过Path属性来匹配URL前缀是/product的请求

这个规则是由org.springframework.cloud.gateway.handle.predicate.PathRoutePredicateFactory来实现的

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

After

这个工厂需要一个日期时间(java的ZoneDateTime对象),匹配指定日期之后的请求

predicates:
  - Path=/product/**
  - After=2025-10-10T10:59:52.139671+08:00[Asia/Shanghai]

此时由于这个时间还未到,因此访问是失败的

Before

匹配指定日期之前的请求,与After一样,需要一个日期时间(java的ZoneDateTime对象)

predicates:
  - Before=2025-10-10T10:59:52.139671+08:00[Asia/Shanghai]

Between

匹配两个指定日期之间的请求,datetime2的参数必须在datetime1之后

predicates:
  - Between=2024-10-09T10:59:52.139671+08:00[Asia/Shanghai],2024-10-10T11:50:52.139671+08:00[Asia/Shanghai]

Cookit

请求中包含指定的Cookie,且该Cookie值指定的正则表达式

predicates:
  - Cookie=test,test1

此时一旦请求不包含cookie,就会无法访问

加上Cookie即可

Header

请求中包含指定的Header,且该Header值符合指定的正则表达式

Host

请求必须是访问某个host(根据请求中的Host字段进行匹配)

predicates:
  - Host=**.hostname1.org,**.hostname2.org

Method

匹配指定的请求方法

predicates:
  - Method=GET,POST

Path

匹配指定路径的规则

predicates:
  - Path=/product/getProductById

RemoteAddr

请求者的IP必须为指定范围

predicates:
  - RemoteAdd=127.0.0.1/8080

6. Gateway Filtee Factories(网关过滤器工厂)

Predicate决定了请求由哪一个路由处理.如果在请求处理前后需要加一些逻辑,那就是Filter(过滤器)的范畴了

Filter过滤器分为两种:Pre类型和Post类型

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

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

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

Spring Cloud Gateway从作用范围上,把Filter分为GatewayFilter和GlobalFilter

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

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

6.1 GatewayFilter

GatewayFilterPredicate类似,都是在application配置文件中进行配置

每个过滤器的功能都是固定的,比如AddRequestParamterGatewayFilterFactory只需要在配置文件中写AddRequestParameter,就可以为所有的请求添加一个参数

predicates:
    filters:
      - AddRequestParameter=userName,zhangsan

我们在product-service中接受到请求的时候,将请求参数打印出来

public ProductInfo getProductById(@RequestParam int id,@RequestParam("userName") String userName) {
    log.info("接收到请求参数"+userName);
    log.info("getProductById");
    return productService.getProductById(id);
}

进行访问:

观察日志:

Spring Cloud Gateway 提供的Filter非常多,以下是一些常见的:

AddRequestHeader

为当前请求添加Header

- AddRequestHeader=X-Request-red, blue

AddRequestParameter

为当前请求添加请求参数

- AddRequestParameter=red, blue

AddResponseHeader

为响应结果添加Header

- AddResponseHeader=X-Response-Red, Blue

RemoveRequestHeader

从当前请求删除某个Header

- RemoveRequestHeader=X-Request-Foo

RemoveResponseHeader

从响应结果删除某个Header

- RemoveResponseHeader=X-Response-Foo

RequestRateLimiter

为当前网关的所有请求执行限流过滤,如果被限流,默认会响应HTTP429-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

Retry

针对不同的响应进行重试,当后端服务器不可用时,网关会根据配置参数来发起重试请求

filters:
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY

retries:重试次数,默认为3

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

RequestSize

设置允许接收请求的最大请求包的大小,如果请求包大小超过设置的值,则返回413 PayLoad Too Large.

请求包大小,单位为字节,默认值为5M

  filters:
    - name: RequestSize
      args:
        maxSize: 5000000

默认过滤器Default Filters

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

配置:

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Red, Default-Blue
      - PrefixPath=/httpbin

6.2 GlobalFilter

GlobalFilter 是Spring Cloud Gateway中的全局过滤器,它和GatewayFilter的作用是相同的

GlobalFilter会应用到所有的路由请求上,全局过滤器通常用于实现与安全性,性能监控和日志记录等相关的全局功能

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

  • Gateway Metrics Filter:网关指标,提供监控指标
  • Forward Routing Filter:用于本地forward,请求不转发到下游服务器
  • LoadBalance Client Filter:针对下游服务器,实现负载均衡

快速上手

  1. 添加依赖
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 添加配置
spring:
    gateway:
      metrics:
        enabled: true
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always
    shutdown:
      enabled: true
  1. 测试

访问http://127.0.0.1:10030/actuator,就能看到所有监控的信息链接

7. 过滤器执行顺序

当一个项目里面,既有GatewayFilter,又有GlobalFilter时,请求路由后,网关会把当前项目中的GatewayFilter和GlobalFilter合并到一个过滤器链中,并进行排序,依次执行过滤器

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

  • Filter通过实现Order接口或者添加@Order注解来指定order值
  • Spring Cloud Gateway提供的Filter由Spring 指定,用户也可以自定义Filter,由用户自己指定
  • 当过滤器的order值一样时,会按照defaultFilter > GatewayFilter > GlobalFilter的顺序执行

8. 自定义过滤器

Spring Cloud Gateway提供了过滤器的拓展功能,开发者可以根据业务来自定义过滤器,同样自定义过滤器也支持GatewayFilter和GlobalFilter两种

8.1 自定义GatewayFilter

实现对应的接口GatewayFilterFactory,Spring Boot默认帮我们实现的2抽象类是AbstractGatewayFilterFactory

定义GatewayFilter

@Slf4j
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {
    public CustomGatewayFilterFactory() {
        super(CustomConfig.class);
    }
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE; // 最低级
    }

    @Override
    public GatewayFilter apply(CustomConfig config) {
        return new GatewayFilter() {
            /*
            ServerWebExchange -> 提供了对HTTP请求和响应的访问
            GatewayFilterChain: 过滤器链
            Mono: Reactor的核心类,数据流发布者,Mono最多只能触发一个事件,可以把Mono用在异步完成任务时候,发出通知
            chain.filter(exchange) 执行请求
            Mono.fromRunnable() 创建一个包含Runnable元素的数据流
             */
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                log.info("Pre Filter,config:{}",config); // Pre类型过滤器
                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                    log.info("Post Filter,config:{}",config);
                }));
            }
        };
    }
}

注意:

  1. CustomConfig是一个配置类,该类的属性就是后面从yaml配置文件中提取的配置

  1. 类名统一以GatewayFilterFactory结尾,因为默认情况下,过滤器的name会采用该定义类的前缀 如在本例中的custom
  2. 该类需要交给Spring 管理,需要添加注解如@Component

配置

filters:
  - AddRequestParameter=userName,zhangsan
  - name: Custom
    args:
      name: custom filter

测试

访问接口后

8.2 自定义GlobalFilter

GlobalFilter的实现比较简单,不需要额外的配置,只需要实现GlobalFilter接口

定义GlobalFilter

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

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

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

测试


原文地址:https://blog.csdn.net/m0_60963435/article/details/142829263

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!