Spring Cloud Gateway

文章目录
  • 一. 概念
  • 二. 常见网关对比
  • 三. 流量网关与业务网关
  • 四. 作用
      1. 路由转发
        1. 模拟发送Http请求
        1. 实现过程
      1. 负载均衡
      1. 统一处理跨域
      • 3.1 跨域的概念
      • 3.2 跨域带来的问题
      • 3.3 解决方案-配置式
      1. 接口保护
      • 4.1 限制请求
      • 4.2 信息脱敏
      • 4.3 降级(熔断)
          1. 概念
          1. 实现方式-配置式
          • 2.1 引入依赖
          • 2.2 熔断配置
          • 2.3 返回友好提示
      • 4.4 限流
          1. 概念
          1. 常见限流算法
          • 2.1 漏桶算法
            • 2.1.1 概念
            • 2.1.2 核心思想
            • 2.1.3 不足
            • 2.1.4 算法描述
          • 2.2 令牌桶算法
            • 2.2.1 概念
            • 2.2.2 核心思想
            • 2.2.3 算法描述
          • 2.3 令牌桶和漏桶对比
          1. 基于Redis的限流方案
          • 3.1 实现思路
          • 3.2 操作步骤
              1. 引入依赖
              1. 配置KeyResolver-指定限流的 Key
              1. 配置限流的过滤器信息
              1. 测试结果
              1. Redis存储内容
      • 4.5 超时时间-连接超时和响应超时
          1. 概念
          1. 实现方式-配置式
          • 2.1 全局配置-为所有路由设置统一的超时标准
          • 2.2 路由配置-为特定的路由设置不同的超时参数,以满足不同服务的特定需求
      • 4.6 重试(业务保护)
          1. 概念
          1. 实现方式-配置式
      • 4.7 访问控制
          1. 概念
          1. 实现方式
      1. 安全认证-自定义全局过滤器 实现GlobalFilter接口

一. 概念

API网关:指系统的统一入口,对内部系统进行封装,所有请求都先经过网关,由网关将请求路由到合适的微服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、日志、路由转发等。

二. 常见网关对比

三. 流量网关与业务网关

流量网关

业务网关

代表

Nginx

Spring Cloud Gateway

定位

主要是作为一个高性能的Web服务器和反向代理服务器,侧重于静态内容的高速分发、负载均衡、SSL终止

为微服务架构设计,是API管理平台的一部分,提供更高级的API管理功能

功能

提供HTTP服务器功能,反向代理,负载均衡,简单的访问控制和静态内容缓存等。

除了负载均衡、路由分发之外,还支持API聚合、协议转换、认证鉴权、限流熔断、监控日志等功能,特别适合微服务架构的复杂需求。

配置

配置基于文本的配置文件,灵活性和控制力强,但配置复杂度相对较高

支持基于YAML或Java配置

适用场景

适用于需要高性能Web服务器、简单API路由、负载均衡的场景

微服务架构中,特别是需要复杂API管理、安全控制、监控和动态路由的场景。

总结

Nginx作为前端的负载均衡器

Gateway对后端服务进行更细粒度的API管理。

四. 作用

1. 路由转发

客户端或者sdk发送的http请求都是指向网关,再转发到实际的API接口。

1. 模拟发送Http请求
复制代码
/**
     * 获取随机文本
     * @return
     */
// 向网关服务发送请求
public String getRandomWork(){
    return HttpRequest.get("http://localhost:8090/api/interface/random/word")
    .addHeaders(getHeadMap("",accessKey,secretKey))
    .execute().body();
}
2. 实现过程
  1. 微服务启动,将自己注册到Nacos,Nacos记录了各微服务实例的地址.

  2. 网关从Nacos读取服务列表,包括服务名称、服务地址等.

  3. 请求到达网关,网关将请求路由到具体的微服务.

  4. 通过网关配置文件中制定的路由规则

    spring:

    application:

    name: api-gateway

    cloud:

    nacos:

    discovery:

    server-addr: 127.0.0.1:8848

    网关路由配置

    gateway:

    routes:

    路由id,自定义,唯一即可。

    • id: api_backend

    多种方式配置目标地址

    uri: http:// 127.0.0.1:9001 http方式:固定地址

    uri: lb://api-backend #lb就是负载均衡,后面跟服务名称

    predicates: #路由断言,判断请求是否符合路由规则的条件

    • Path=/api/** # 以/api开头的请求就会被转发到api-backend服务

2. 负载均衡

复制代码
在路由的基础上,将请求转发到集群A的某一台机器上,再调用机器上对应的接口
uri: lb://api-backend #lb就是负载均衡

3. 统一处理跨域

3.1 跨域的概念

如果一个请求的协议、主机或端口任意一个与当前页面的源(即协议、主机和端口)不同,则该请求就被认为是跨域请求。

3.2 跨域带来的问题
  1. AJAX请求被拒绝:浏览器会阻止在跨域请求中使用XMLHttpRequest对象进行通信。
  2. Cookie不可用:跨域请求默认不会发送源站的Cookie信息,导致无法验证用户身份。
  3. 访问被拒绝:跨域请求可能会受到服务器的访问控制策略阻止。
3.3 解决方案-配置式
复制代码
spring:
	cloud:
		gateway:
			globalcors:
				corsConfigurations:
					'[/**]':
						allowedOrigins: "*"
						exposedHeaders:
						- content-type
						allowedHeaders:
						- content-type
						allowCredentials: true
						allowedMethods:
						- GET
						- OPTIONS
						- PUT
						- DELETE
						- POST

4. 接口保护

4.1 限制请求
4.2 信息脱敏
4.3 降级(熔断)
1. 概念

熔断是一种防止故障扩散的策略。当一个服务出现故障或超时,熔断器会打开并快速失败,拒绝后续的请求,避免请求堆积和资源耗尽。熔断器会暂时屏蔽该服务,并在一段时间后尝试恢复。熔断器的状态变化可用于监控系统健康和提供告警信息。

2. 实现方式-配置式
2.1 引入依赖
复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.2 熔断配置
复制代码
  spring:
     cloud:
       gateway:
         routes:
           - id: your_route_id
             uri: lb://your_service_id
             filters:
               - name: Hystrix
                 args:
                   name: fallback
                   fallbackUri: forward:/fallback  # 当熔断发生时转发到的本地fallback处理逻辑
             predicates:
               - Path=/api/** # 例如,匹配所有/api开头的路径

上面配置了一个 Hystrix 过滤器,该过滤器会使用 Hystrix 熔断与回退,原理是将请求包装成 RouteHystrixCommand 执行,RouteHystrixCommand 继承于 com.netflix.hystrix.HystrixObservableCommand。

fallbackUri 是发生熔断时回退的 URI 地址,目前只支持 forward 模式的 URI。如果服务被降级,该请求会被转发到该 URI 中。

2.3 返回友好提示
复制代码
@RestController
public class FallbackController {
    @GetMapping("/fallback")
    public String fallback() {
        throw new RunTimeException("请稍后再试!");
    }
}
4.4 限流
1. 概念

API 网关作为所有请求的入口,请求量大,我们可以通过对并发访问的请求进行限速来保护系统的可用性。

2. 常见限流算法
2.1 漏桶算法
2.1.1 概念

水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率

2.1.2 核心思想

可见这里有两个变量,一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate)。

2.1.3 不足

因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流量突发(burst)到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。

2.1.4 算法描述
复制代码
1.  一个固定容量的漏桶,按照常量固定速率流出水滴;

2.  如果桶是空的,则不需流出水滴;

3.  可以以任意速率流入水滴到漏桶;

4. 如果流入水滴超出了桶的容量,则流入的水滴溢出了(被丢弃),而漏桶容量是不变的。
2.2 令牌桶算法
2.2.1 概念

系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token,如果桶已经满了就不再加了。新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务。

2.2.2 核心思想

令牌桶的核心是可以预先在令牌桶中存储一些Token,这样当流量激增的时候,可以并发处理这一批请求,而且一旦需要提高速率,则按需提高放入桶中的令牌的速率.

2.2.3 算法描述
  1. 假设限制2r/s,则按照500毫秒的固定速率往桶中添加令牌;

  2. 桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝;

  3. 当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上;

  4. 如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待)。

2.3 令牌桶和漏桶对比
  1. 令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求;

  2. 漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝;

  3. 令牌桶限制的是平均流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿3个令牌,4个令牌),并允许一定程度突发流量;

  4. 漏桶限制的是常量流出速率(即流出速率是一个固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2),从而平滑突发流入速率;

  5. 令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率;

  6. 两个算法实现可以一样,但是方向是相反的,对于相同的参数得到的限流效果是一样的。

3. 基于Redis的限流方案
3.1 实现思路

spring cloud gateway默认基于redis令牌桶算法进行微服务的限流保护,采用RateLimter限流算法来实现。

3.2 操作步骤
1. 引入依赖
复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
2. 配置KeyResolver-指定限流的 Key
复制代码
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;

/**
 * 限流配置KeyResolver------有三种写法(接口限流/ip限流/用户限流)
 */
@Configuration
public class RateLimiteConfig {

    /**
     * 接口限流:根据请求路径限流
     * 如果不使用@Primary注解,会报错
     * @return
     */
    @Bean
    @Primary
    public KeyResolver pathKeyResolver() {
       return exchange -> Mono.just(exchange.getRequest().getPath().toString());
    }

    /**
     * 根据请求IP限流
     * @return
     */
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()
        );
    }

    /**
     * 根据请求参数中的userId进行限流
     * 请求地址写法:http://localhost:8801/rate/123?userId=lisi
     * @return
     */
    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId")
        );
    }
}
3. 配置限流的过滤器信息
复制代码
1. filter 名称必须是 RequestRateLimiter。
2. redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求。
3. redis-rate-limiter.burstCapacity:令牌桶的容量,允许在 1s 内完成的最大请求数。
4. key-resolver:使用 SpEL 按名称引用 bean。


spring:
  cloud:
    gateway:
      routes:
        - id: rate-limit-demo
          uri: lb://mima-cloud-producer
          predicates:
            #访问路径:http://localhost:8801/rate/123
            - Path=/rate/**
          filters:
            - name: RequestRateLimiter
              args:
                # 令牌桶每秒填充平均速率, 允许用户每秒处理多少个请求。
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的容量,允许在1s内完成的最大请求数。
                redis-rate-limiter.burstCapacity: 2
                # 使用SpEL表达式从Spring容器中获取Bean对象, 查看RateLimiteConfig实现类中的方法名
                key-resolver: "#{@pathKeyResolver}"
                #key-resolver: "#{@ipKeyResolver}"
                #key-resolver: "#{@userKeyResolver}"
4. 测试结果

多次调用请求,控制台打印结果

复制代码
[开始]请求路径:/rate/123
[应答]请求路径:/rate/123耗时:2ms
2024-09-08 16:23:27.253 DEBUG 18512 --- [ioEventLoop-4-1] o.s.w.s.adapter.HttpWebHandlerAdapter    : [62eb90e0] Completed 429 TOO_MANY_REQUESTS
2024-09-08 16:23:27.394 DEBUG 18512 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter    : [62eb90e0] HTTP GET "/rate/123"
corsFilter... run
[开始]请求路径:/rate/123
[应答]请求路径:/rate/123耗时:2ms
2024-09-08 16:23:27.397 DEBUG 18512 --- [ioEventLoop-4-1] o.s.w.s.adapter.HttpWebHandlerAdapter    : [62eb90e0] Completed 429 TOO_MANY_REQUESTS
2024-09-08 16:23:27.536 DEBUG 18512 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter    : [62eb90e0] HTTP GET "/rate/123"
corsFilter... run
5. Redis存储内容
复制代码
当发生限流时,会向redis中存储两个数据

127.0.0.1:6379> keys *
 1) "request_rate_limiter.{localhost}.timestamp"
 2) "request_rate_limiter.{localhost}.tokens"

大括号中就是我们的限流 Key,这里是 IP,本地的就是 localhost。

timestamp:存储的是当前时间的秒数,也就是 System.currentTimeMillis()/1000 或者 Instant.now().getEpochSecond()。

tokens:存储的是当前这秒钟对应的可用令牌数量。
4.5 超时时间-连接超时和响应超时
1. 概念

连接超时和响应超时是两种不同的超时机制,分别用于处理不同的网络通信问题。

连接超时(connect-timeout)

  1. 主要指的是在建立网络连接过程中所允许的最大等待时间。
  2. 如果在规定的时间内无法成功建立与服务端的连接,则认为连接超时。
  3. 在Spring Cloud Gateway中,连接超时用于确保在尝试与服务建立连接时,如果等待过长时间仍未成功,则放弃此次连接尝试,避免长时间占用资源。

响应超时(response-timeout)

  1. 是指在建立连接后,等待服务端响应的最大等待时间。
  2. 如果在规定的时间内服务端未返回响应,则认为响应超时。
  3. 这个设置用于确保在请求发送后,如果服务端在合理时间内未作出响应,网关能够及时释放资源,避免因等待过久而造成资源浪费或影响其他请求的处理。
2. 实现方式-配置式
2.1 全局配置-为所有路由设置统一的超时标准
复制代码
spring:
  cloud:
	gateway:
	  httpclient:
		connect-timeout: 1000 # 连接超时,单位是毫秒
		response-timeout: 5s # 响应超时,单位是秒
2.2 路由配置-为特定的路由设置不同的超时参数,以满足不同服务的特定需求
复制代码
    gateway:
      routes:
        # 路由id,自定义,唯一即可。
        - id: api_backend_route
          # 多种方式配置目标地址
          # uri: http:// 127.0.0.1:9001 http方式:固定地址
          uri: lb://api-backend #lb就是负载均衡,后面跟服务名称
          predicates: #路由断言,判断请求是否符合路由规则的条件
            - Path=/api/** # 以/api开头的请求就会被转发到api-backend服务
          metadata:
            response-timeout: 5000
            connect-timeout: 2000
4.6 重试(业务保护)
1. 概念

当网关将请求转发到内部服务时,如果内部服务调用失败,我们可以尝试重新发送请求,从而保证系统的可靠性

2. 实现方式-配置式
复制代码
filters:
  - name: Hystrix
    args:
      name: fallback
      fallbackUri: forward:/fallback  # 当熔断发生时转发到的本地fallback处理逻辑
  - name: Retry # 配置重试过滤器
    args:
      retries: 3 # 重试次数,默认值是 3 次。
      series: SERVER_ERROR # 状态码配置(分段),符合某段状态码才会进行重试逻辑,默认值是 SERVER_ERROR,值是 5,也就是 5XX(5 开头的状态码)
4.7 访问控制
1. 概念

针对访问网关的请求进行限制;比如:特定IP才可以访问该请求-白名单;某个IP在短时间内发送大量请求,从而使得服务器资源耗尽,导致正常用户的合法请求失败-黑名单;

2. 实现方式
  1. 在网关的配置文件中添加请求的黑白名单.

    request:

    white-list:

    • /api/v1/get/test

    • api/v1/get/getUserInfo

    black-list:

    • 127.0.0.1
  2. 通过配置类映射黑白名单信息到集合中

    package com.monkey.gateway_template.config;

    import org.springframework.boot.context.properties.ConfigurationProperties;

    import org.springframework.stereotype.Component;

    import java.util.List;

    /**

    • 请求白名单.

    */

    @Component

    @ConfigurationProperties("request")

    public class RequestWhiteList {

    复制代码
    /**
     * 白名单列表
     */
    List<String> whiteList;
    
    public List<String> getWhiteList() {
        return whiteList;
    }
    
    public void setWhiteList(List<String> whiteList) {
        this.whiteList = whiteList;
    }

    }

  3. 在自定义的全局过滤器中校验黑白名单,实现请求的放行与屏蔽

    @Slf4j

    @Component

    public class LoginGlobalFilter implements GlobalFilter, Ordered {

    @Override

    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

    // 黑白名单

    InetSocketAddress localAddress = request.getLocalAddress();

    if (!"127.0.0.1".equals(localAddress.getHostString())){

    response.setStatusCode(HttpStatus.FORBIDDEN);

    return response.setComplete();

    }

    }

    }

2. 安全认证-自定义全局过滤器 实现GlobalFilter接口

登录认证以及请求接口认证的方法在filter方法中实现

过滤器链

复制代码
   @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 登录认证以及请求接口认证的方法在filter方法中实现
}
相关推荐
晚霞的不甘1 小时前
Flutter for OpenHarmony天气卡片应用:用枚举与动画打造沉浸式多城市天气浏览体验
前端·flutter·云原生·前端框架
苏三说技术2 小时前
xxl-job 和 elastic-job,哪个更好?
后端
xkxnq2 小时前
第五阶段:Vue3核心深度深挖(第74天)(Vue3计算属性进阶)
前端·javascript·vue.js
三小河2 小时前
Agent Skill与Rules的区别——以Cursor为例
前端·javascript·后端
Hilaku2 小时前
不要在简历上写精通 Vue3?来自面试官的真实劝退
前端·javascript·vue.js
三小河2 小时前
前端视角详解 Agent Skill
前端·javascript·后端
牛奔2 小时前
Go 是如何做抢占式调度的?
开发语言·后端·golang
Aniugel2 小时前
单点登录(SSO)系统
前端
颜酱2 小时前
二叉树遍历思维实战
javascript·后端·算法
鹏多多2 小时前
移动端H5项目,还需要react-fastclick解决300ms点击延迟吗?
前端·javascript·react.js