面试官:网关如何实现限流?

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

网关的主要作用有以下几个:

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

1.关于限流

为了保护后端微服务免受突发高流量请求的影响,确保系统的稳定和可靠性,所以在网关层必须"限流"操作。

限流是一种流量控制的策略,用于限制系统处理请求的速率或数量,以保护系统免受过载或攻击的影响。通过限制请求的数量或速率,可以平衡系统和资源之间的压力,确保系统在可接受的范围内运行。

限流的常见策略通常有以下几种:

  1. 请求速率限流:限制单位时间内系统可以接受的最大请求数量。例如,每秒最多处理 100 个请求。当请求超过限制时,可以选择拒绝或延迟处理这些请求。
  2. 并发请求数限流:限制同时处理的请求数量。例如,限制系统只能同时处理100个并发请求。当并发请求数超过限制时,可以选择拒绝或排队等待。
  3. 用户级别限流:根据用户进行限流,限制每个用户的请求频率或数量。例如,限制每个用户每分钟只能发送 10 个请求。当用户请求超过限制时,可以选择拒绝或延迟处理。
  4. API 级别限流:根据 API 接口进行限流,限制每个接口的请求频率或数量。例如,限制某个接口每秒只能处理 50 个请求。当接口请求超过限制时,可以选择拒绝或延迟处理。

当然,我们也可以在程序中使用多种策略混合限流,以保证内部微服务的稳定性。

2.如何实现限流?

了解了网关和限流的相关内容之后,我们以目前主流的网关组件 Spring Cloud Gateway 为例,来实现一下限流功能。

Spring Cloud Gateway 实现限流的方式有两种:

  1. 使用内置 Filter(过滤器)实现限流。
  2. 使用限流组件 Spring Cloud Alibaba Sentinel 或者 Spring Cloud Netflix Hystrix 实现限流。

那既然 Spring Cloud Gateway 中已经内置了限流功能,那我们接下来就来看 Spring Cloud Gateway 内置限流是如何实现的?

Spring Cloud Gateway 内置的限流器为 RequestRateLimiter GatewayFilter Factory,官网说明文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-requestratelimiter-gatewayfilter-factory

Spring Cloud Gateway 支持和 Redis 一起来实现限流功能,它的实现步骤如下:

  1. 在网关项目中添加 Redis 框架依赖
  2. 创建限流规则
  3. 配置限流过滤器

具体实现如下。

2.1 添加 Redis 框架依赖

在项目的 pom.xml 中,添加以下配置信息(添加 Redis 框架依赖支持):

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

2.2 创建限流规则

接下来我们新建一个限流规则定义类,实现一下根据 IP 进行限流的功能,实现示例代码如下:

java 复制代码
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class IpAddressKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().
                         getHostString());
    }
}

这一步其实是在配置限流器的限流参数 KeyResolver,也就是限流功能的依赖"凭证"。

PS:当然,我们还可以通过 URL、方法名、用户等进行限流操作,只需要修改此步骤中的限流凭证,也就是 KeyResolver 即可。

2.3 配置限流过滤器

在网关项目的配置文件中,添加以下配置信息:

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: retry
          uri: lb://nacos-discovery-demo
          predicates:
            - Path=/retry/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 1
                keyResolver: '#{@ipAddressKeyResolver}' # spEL表达式
  data:
    redis:
      host: 127.0.0.1
      port: 16379
      database: 0

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

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

SpEL(Spring Expression Language,Spring 表达式语言)是 Spring 框架中用于提供灵活、强大的表达式解析和求值功能的统一表达式语言。它可以在运行时动态地解析和求值字符串表达式,通常用于配置文件中的属性值、注解、XML 配置等地方。

注意事项

当 Spring Cloud Gateway 配合 Redis 实现限流的时候,它对于 Redis 的版本是有要求的,因为它在限流时调用了一个 Redis 高版本的函数,所以 Redis Server 版本太低,限流无效,Redis Server 最好是 5.X 以上。

2.4 限流测试

最后,我们频繁的访问:http://localhost:10086/retry/test 就会看到如下限流信息:

3.限流实现算法

Spring Cloud Gateway 内置限流功能使用的算法是令牌桶限流算法

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

令牌桶执行流程如下图所示:

常见的限流算法还有:计数器算法、滑动计数器算法、漏桶算法等,更多介绍参考我之前写个的文章:https://www.javacn.site/interview/springcloud/loadbalancer.html

4.限流实现原理

Spring Cloud Gateway 执行过程如下图所示:

从图中可以看出,所有的请求来了之后,会先走过滤器,只有过滤器通过之后,才能调用后续的内部微服务,这样我们就可以通过过滤器来控制微服务的调用,从而实现限流功能了。

Spring Cloud Gateway 过滤器是基于令牌桶算法来限制请求的速率,该过滤器根据配置的限流规则,在指定的时间窗口内分配一定数量的令牌,每个令牌代表一个允许通过的请求,当一个请求到达时,如果没有可用的令牌,则请求将被阻塞或拒绝。

令牌桶的执行过程如下:

  1. 初始化:在加载过滤器工厂时,会基于给定的限流规则创建一个限流器,该限流器包含了令牌桶算法的逻辑。默认情况下,令牌桶是按照固定速率进行填充,也可以配置为令牌桶按照令牌令牌的方式进行填充。
  2. 请求处理:每当有请求进来时,限流器会检查当前令牌桶中是否有可用的令牌。如果有可用的令牌,则请求会被放行,令牌桶中的令牌数量减少;如果没有可用的令牌,则请求会被阻塞或拒绝。
  3. 令牌桶填充:限流器会定期填充令牌桶,即向令牌桶中添加新的令牌。填充的速率取决于限流规则中配置的速率值。
  4. 令牌桶容量控制:限流器还会根据限流规则中配置的令牌桶容量,控制令牌桶中的令牌数量。如果令牌桶已满,则多余的令牌会被丢弃。

小结

主流网关组件 Spring Cloud Gateway 实现限流的方式主要有两种:内置限流过滤器和外部限流组件,如 Sentinel、Hystrix 等。而最简单的限流功能,我们只需要使用 Spring Cloud Gateway 过滤器 + Redis 即可(实现),其使用的是令牌桶的限流算法来实现限流功能的。

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。