Spring Cloud Gateway 入门与实战

一、网关

在微服务框架中,网关是一个提供统一访问地址的组件,它充当了客户端和内部微服务之间的中介。网关主要负责流量路由和转发,将外部请求引导到相应的微服务实例上,同时提供一些功能,如身份认证、授权、限流、监控、日志记录等。

二、网关作用

  1. **路由功能:**网关可以根据目标地址的不同,选择最佳的路径将数据包从源网络路由到目标网络。它通过维护路由表来确定数据包的转发方向,并选择最优的路径。
  2. **安全控制:**网关可以实施网络安全策略,对进出的数据包进行检查和过滤。它可以验证和授权来自源网络的数据包,并阻止未经授权的访问。防火墙是一种常见的网关设备,用于过滤和保护网络免受恶意攻击和未经授权的访问。
  3. **协议转换:**不同网络使用不同的通信协议,网关可以进行协议转换,使得不同网络的设备可以互相通信。例如:将HTTPS 协议转换成 HTTP 协议。
  4. **网络地址转换(NAT):**网关可以执行网络地址转换,将内部网络使用的私有 IP 地址转换为外部网络使用的公共 IP 地址,以实现多台计算机共享一个公共 IP 地址上网

三、Spring Cloud Gateway 组成

  1. **路由:**定义了请求应该被转发到哪个目标地址。路由由 ID、目标 URI 、断言、和过滤器组成。通过配置多个路由,可以实现不同请求的路由规则。
  2. **断言:**用于匹配请求条件,如果请求匹配断言条件,则会被映射到对应目标地址上。断言可以基于请求的路径、请求头、请求参数等信息进行匹配。
  3. **过滤器:**用于在请求路由前或路由后进行一些处理,如添加头部信息、修改请求体等。过滤器可以在全局范围或特定路由范围内配置,多个路由器可以组成过滤器链。

四、Spring Cloud Gateway 的使用

使用步骤如下:

1. 添加 Gateway 依赖

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

2. 设置网关路由规则

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service
        uri: http://192.168.146.1:8082 #映射的ip和端口
        predicates:
        - Path=/user/**,/log/** #当请求路径满足配置的path时,将ip和端口映射到上面的uri

五、断言(Predicate)类型

Spring Cloud Gateway 支持的断言类型目前有 12 中, 包含以下这些:

  1. 根据时间匹配:
  • After: 请求在指定时间之后才匹配
  • Before:请求在指定时间之前才匹配
  • Between:请求在指定时间中间才匹配
  1. Cookie:匹配求中的Cookie值

  2. Header:匹配请求中的 Header 值

  3. Host:匹配请求中的 Host 值

  4. Method:匹配请求头中的 Method 的值

  5. Path:匹配请求路径

  6. Query:匹配请求参数

  7. RemoteAddr:匹配请求的 IP 地址,支持 IPV4 和 IPV6

  8. Weight:根据权重来分发请求,权重根据 group 来计算

  9. XForwardedRemoteAddr:根据 X-Forwarded-For 匹配

5.1 根据时间匹配

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service
        uri: http://192.168.146.1:8082
        predicates:
        - Path=/user/**,/log/**
        - Before=2024-07-15T19:00:00.000+08:00[Asia/Shanghai] #上海时间2024-7-15 19点前允许访问

between

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service
        uri: http://192.168.146.1:8082
        predicates:
        - Path=/user/**,/log/**
        - Between=2024-07-13T19:00:00.000+08:00[Asia/Shanghai],2024-07-15T19:00:00.000+08:00[Asia/Shanghai] #上海时间2024-7-13 19点 ~ 2024-7-15 19点之间允许访问

5.2 根据 Header 匹配

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service
        uri: http://192.168.146.1:8082
        predicates:
        - Path=/user/**,/log/**
        - Between=2024-07-13T19:00:00.000+08:00[Asia/Shanghai],2024-07-15T19:00:00.000+08:00[Asia/Shanghai] #上海时间2024-7-15 19点前允许访问
        - Header= Ip2contry,\w+ 

其中 "Ip2contry" 表示 Header 中的 key ,而 "\w+" 表示的是key 的值,值可用正则表达式进行匹配

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service
        uri: http://192.168.146.1:8082
        predicates:
        - Path=/user/**,/log/**
        - Between=2024-07-13T19:00:00.000+08:00[Asia/Shanghai],2024-07-15T19:00:00.000+08:00[Asia/Shanghai] #上海时间2024-7-15 19点前允许访问
        - Header= Ip2contry,\w+
        - Cookie= author,zhangsan

5.4 根据 weight 进行配置

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service
        uri: http://169.254.165.28:8081
        predicates:
        - Weight=group2,10
      - id: user-service2
        uri: http://169.254.165.28:8082
        predicates:
        - Weight=group2,90

group2 表示分组,10 表示所在分组所占权重

六、Spring Cloud Gateway + Nacos + LoadBalancer 实现企业级网关

6.1 添加依赖

XML 复制代码
<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<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-gateway</artifactId>
</dependency>

6.2 设置路由规则

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service
        uri: lb://user-service #user-service #调用服务注册在 nacos 中的名称
        predicates:
        - Path=/user/**
    nacos:
      discovery:
        server-addr: localhost:8848
        password: nacos
        username: nacos
        register-enabled: false

七、过滤器

过滤器作用:

  • **功能扩展和定制:**过滤器可以用于对现有功能进行扩展和定制。通过拦截和处理数据流或事件流,可以修改数据,增加额外的功能逻辑,实现特定的业务需求。例如,可以使用过滤器在请求之前进行身份验证、权限控制,或者在响应之后进行日志记录、数据转换等操作。
  • **数据校验和过滤:**过滤器可以用于对数据进行校验和过滤。在接收到数据之后,可以使用过滤器对数据进行检查,验证数据的合法性,过滤掉无效或不符合要求的数据。这有助于保证数据的准确性、完整性和安全性。
  • **安全保护:**过滤器可以用于提供安全保护措施,可以使用过滤器对输入的请求进行检查和清洗,以防止潜在的安全漏洞,例如跨站脚本攻击(XSS)、跨站请求伪造(CSRF) 等攻击。过滤器还可以进行访问控制,验证权限和实施安全策略。
  • 性能优化:过滤器可以用于性能优化。例如,在数据处理流水线中,可以使用过滤器对数据进行转换、过滤或缓存,以提高处理速度和效率。过滤器还可以用于数据压缩、缓存预热等场景,减少数据传输和处理的成本。
  • 统一处理和逻辑服用:过滤器提供了一种统一的处理方式,可以在不同的组件或模块上应用相同的逻辑或处理方式。通过将处理逻辑抽象为过滤器,可以避免重复代码、统一错误处理和统一异常处理等,提高代码复用性和可维护性。

7.1 内置局部过滤器

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service-demo
        uri: lb://user-service
        predicates:
        - Path=/user/**
        filters:
        - AddResponseHeader=soruce,userservice

其中 "AddResponseHeader" 表示添加返回头过滤器,前面的值 " source" 为 key ,后面的值 "value" 为 value

7.2 AddRequestHeader

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service-demo
        uri: lb://user-service
        predicates:
        - Path=/user/**
        filters:
        - AddResponseHeader=soruce,userservice
        - AddRequestHeader= my-request-market, kaikai

在下游服务调用接口中打印所有的 Header 查看添加的 Header 信息,代码如下:

java 复制代码
@RequestMapping("/getHeader")
public void getHeader(HttpServletRequest request){
    Enumeration<String> headers= request.getHeaderNames();
    while (headers.hasMoreElements()){
        String key=headers.nextElement();
        String value= request.getHeader(key);
        System.out.println(key+ " : "+value);
    }
}

7.3 AddRequestParameter

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service-demo
        uri: lb://user-service
        predicates:
        - Path=/user/**
        filters:
        - AddRequestParameter= id,209

后续请求中带有key为id,value 209 的参数

java 复制代码
    @RequestMapping("/getID")
    public Integer getID(@RequestParam("id")Integer id){
        return id;
    }

    @RequestMapping("/getId")
    public String getId(HttpServletRequest request){
        return request.getParameter("id");
    }

7.4 PrefixPath

在请求的 url 前面添加前缀,例如请求的是 /user, 添加了 PrefixPath为 /new,那么后续访问的地址就为 "/new/user"

7.5 限流过滤器- RequestRateLimiter

网关限流过滤器,Spring Cloud Gateway 内置了限流功能,它使用的限流算法是令牌桶的限流算法

**令牌桶限流算法:**令牌安固定的速率被放入令牌桶中,桶中最多存放 N个令牌(Token),当桶装满时,新添加的令牌被丢弃或拒绝。当请求到达时,将从桶中删除 1 个令牌。令牌中的令牌不仅可以被移除,还可以往里添加,所以为了保证接口随时有数据通过,必须不停地往桶里加令牌。由此可见,往桶里加令牌的速度就决定了数据通过接口的速度。我们通过控制往令牌桶里加令牌的速度从而控制接口的流量。

Spring Cloud Gateway 当前版本支持 和 Redis 一起实现限流功能,它的实现步骤总共分为以下三步:

1. 添加 Reids 框架依赖

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

2. 创建限流规则

创建一个类,根据 IP 进行限流:

java 复制代码
@Component
public class IpKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        System.out.println(exchange.getRequest().getRemoteAddress().getHostString());
        System.out.println(exchange.getRequest().getRemoteAddress().getHostName());
        return Mono.just(exchange.getRequest().getRemoteAddress().getHostString());
    }
}

3. 配置限流规则

在项目的配置文件中,配置以下内容:

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service-demo
        uri: lb://user-service
        predicates:
        - Path=/user/**
        filters:
          - name: RequestRateLimiter
            args:
               redis-rate-limiter.replenishRate: 1
               redis-rate-limiter.burstCapacity: 1
               keyResolver: '#{@ipKeyResolver}'
    nacos:
      discovery:
        username: nacos
        password: nacos
        server-addr: localhost:8848
  data:
    redis:
      host: 114.115.149.19
      port: 6378
      database: 0

其中,name 必须等于 "RequestRateLimiter" 内置限流过滤器,其他参数:

  • redis-rate-limiter.replenishRate:令牌填充速度:每秒允许请求数
  • redis-rate-limiter.burstCapacity: 令牌桶容量:最大令牌数
  • redis-rate-limiter.requestedTokens: 每次请求消耗的令牌数
  • keyResolver: 根据哪个 key 进行限流,它的值时 spEL 表达式。

7.6 Retry

配置:

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service-demo
        uri: lb://user-service
        predicates:
        - Path=/user/**
        filters:
        - name: Retry
          args:
            retries: 3 #重试次数
            statuses: Gateway Timeout #状态码,详情参考 HttpResponseStatus.class
            method: GET
#            series: CLIENT_ERROR
            backoff:
              firstBackoff: 1000ms #第一次重试间隔
              maxBackoff: 10000ms   #最大重试间隔时间
              factor: 2 #firstBackoff * (factor ^ n) 重试系数,如果重试间隔时间超过最大重试间隔时间,按照最大重试间隔时间来算
              basedOnPreviousValue: false #根据上次重试时间加上重试系数来计算

代码调试:

java 复制代码
    @RequestMapping("/getstatus")
    public void getStatus(HttpServletResponse response){
        System.out.println("==============Do GetStatus Method ===================");
        response.setStatus(403);
    }
  • name :一定要等于 "Retry" ,因为 "Retry" 就是内置的过滤器的名字
  • retries: 重试次数
  • statuses:状态码 ,匹配对应状态码响应,并重试
  • series: 状态码配置,符合某段状态码才会进行重试逻辑,默认值是SERVER_ERROR,值是 5 ,也就是 5XX 开头的状态码。
    1XX:INFORMATIONAL
    2XX:SUCCESSFUL
    3XX:REDIRECTION
    4XX:CLIENT_ERROR
    5XX:SERVER_ERROR
  • backoff:重试指数配置策略,默认关闭

注:statuses 和 retries 是且的关系。

7.7 内置全局过滤器

bash 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: user-service-demo
        uri: lb://user-service
        predicates:
        - Path=/user/**
        filters:
#        - AddResponseHeader=soruce,userservice
      default-filters:
        - AddResponseHeader=soruce,userservice

7.8 自定义过滤器

java 复制代码
package com.gatewayservicesoa.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class AuthFilter implements GlobalFilter, Ordered {
    /**
     * @param exchange 要执行的事件
     * @param chain    过滤器链
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //此处使用的是相应试 HTTP
        ServerHttpRequest request=  exchange.getRequest();
        ServerHttpResponse response=  exchange.getResponse();
        if(request.getQueryParams().getFirst("name")!=null && request.getQueryParams().getFirst("name").equals("name")
                && request.getQueryParams().getFirst("password")!=null && request.getQueryParams().getFirst("password").equals("123")){
            return chain.filter(exchange);
        }else {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
    }

    @Override
    public int getOrder() {
        //过滤器的执行顺序,值越小,越早执行
        return 0;
    }
}

八、工作原理

客户但向 Spring Cloud Gateway 发出请求,网关映射处理程序(Gateway Handler Mapping) 会根据请求路径进行路由匹配,然后再将其发送到网关 Web 处理器(Gateway Web Handler) 进行处理。此时处理器会经过过滤链(filter) 进行处理,而过滤器又分为 前置过滤器和后置过滤器,之后才会将请求发送给目标服务(也称代理的服务)。

相关推荐
前行的小黑炭43 分钟前
设计模式:为什么使用模板设计模式(不相同的步骤进行抽取,使用不同的子类实现)减少重复代码,让代码更好维护。
android·java·kotlin
Java技术小馆1 小时前
如何设计一个本地缓存
java·面试·架构
数据智能老司机2 小时前
CockroachDB权威指南——SQL调优
数据库·分布式·架构
数据智能老司机2 小时前
CockroachDB权威指南——应用设计与实现
数据库·分布式·架构
XuanXu2 小时前
Java AQS原理以及应用
java
数据智能老司机2 小时前
CockroachDB权威指南——CockroachDB 模式设计
数据库·分布式·架构
风象南4 小时前
SpringBoot中6种自定义starter开发方法
java·spring boot·后端
mghio13 小时前
Dubbo 中的集群容错
java·微服务·dubbo
咖啡教室18 小时前
java日常开发笔记和开发问题记录
java