ApiBoot 实现分布式令牌桶方式限流

ApiBoot RateLimiter基于拦截器的实现,封装了Google令牌桶方式的限流实现,可通过注解配置每个接口的流量QPS(每秒允许的流量请求)。

注意:目前不支持分布式系统的流量限制,会在下个版本进行更新添加。

添加依赖

  1. <!--ApiBoot RateLimiter-->
  2. <dependency>
  3. <groupId>org.minbox.framework</groupId>
  4. <artifactId>api-boot-starter-rate-limiter</artifactId>
  5. </dependency>

注意:如果未添加ApiBoot版本依赖,请访问版本依赖查看添加方式。

相关配置

配置名称默认值描述
api.boot.rate-limiter.interceptor-url/**数组类型,可配置多个限流的路径地址
api.boot.rate-limiter.enable-global-qpsfalse是否开启全局限流配置
api.boot.rate-limiter.global-qps10全局限流QPS

QPS定义

限流注解RateLimiter配置使用如下所示:

  1. @RestController
  2. @RequestMapping(value = "/resource")
  3. public class ResourceSampleController {
  4. /**
  5. * QPS = 10
  6. * 配置该接口每秒只允许访问10次
  7. *
  8. * @return
  9. */
  10. @RequestMapping(value = "/")
  11. @RateLimiter(QPS = 10)
  12. public UserInfo user() {
  13. return new UserInfo("admin");
  14. }
  15. }

单服务限流

对于单个服务的场景使用限流时,ApiBoot RateLimiter内部使用Google Guava采用令牌桶的方式实现,具体源码实现详见GoogleGuavaRateLimiter类。

引用ApiBoot RateLimiter的项目如果并未添加spring-boot-starter-data-redis依赖,项目中并未初始化RedisTemplate时则会采用Google Guava方式来进行限流。

分布式服务限流

如果你采用微服务、负载均衡的方式进行部署服务时,单服务限流是无法完成预期的效果的,ApiBoot RateLimiter内部集成了Redis方式来自动完成限流,使用Redis后期也利于扩展,如果应用程序过大也可以搭建Redis 集群完成限流。

ApiBoot RateLimiter内部的Redis使用了SpringCloud GateWay官方用的Lua脚本来保证限流的原子性、线程安全性。

使用Redis方式很简单,只需要在项目中添加Redis依赖后进行配置后ApiBoot RateLimiter就会自动通过RedisTemplate来操作Lua脚本,步骤如下所示:

第一步:添加Redis依赖

  1. <!--Redis-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-redis</artifactId>
  5. </dependency>

第二步:配置Redis

  1. spring:
  2. # redis 相关配置
  3. redis:
  4. password: 123456

如果你所使用的Redis有密码,这里需要进行配置,其他配置参数使用默认值。

测试限流

当我们访问被限流的接口时,ApiBoot RateLimiter会自动向Redis写入两条数据,key如下所示:

  1. // redis key list
  2. qps_rate_limiter.{/test/}.timestamp
  3. qps_rate_limiter.{/test/}.tokens
  • qps_rate_limiter.{/test/}.timestamp:存放上一次请求的时间戳
  • qps_rate_limiter.{/test/}.tokens:存放剩余的请求令牌数量key的格式为qps_rate_limiter.{xxx}.timestampqps_rate_limiter.{xxx}.tokens

其中xxx为请求接口的路径。

当然单服务实例也可以使用Redis方式

自定义流量溢出响应

流量被限制后可以自定义响应的实体来告知请求发起端,方便做一些业务性质的处理,如下所示:

  1. import org.minbox.framework.api.boot.common.model.ApiBootResult;
  2. import org.minbox.framework.api.boot.plugin.rate.limiter.result.RateLimiterOverFlowResponse;
  3. import org.springframework.stereotype.Component;
  4. /**
  5. * 自定义流量溢出后响应的实体格式
  6. *
  7. * @author:恒宇少年 - 于起宇
  8. * <p>
  9. * DateTime:2019-05-25 16:21
  10. * Blog:http://blog.yuqiyu.com
  11. * WebSite:http://www.jianshu.com/u/092df3f77bca
  12. * Gitee:https://gitee.com/hengboy
  13. * GitHub:https://github.com/hengboy
  14. */
  15. @Component
  16. public class CustomerResponse implements RateLimiterOverFlowResponse {
  17. @Override
  18. public Object overflow(Object[] methodArgs) {
  19. return ApiBootResult.builder().errorCode("REQUEST_OVER_FLOW").errorMessage("流量被限制.").build();
  20. }
  21. }

overflow方法可返回任意类型对象。

配置中心支持

为了保证有前瞻性突发流量的处理,ApiBoot RateLimiter支持了外部配置中心,在配置中心修改接口限流QPS后会实时更新到应用程序内。

Nacos 配置中心

如果你想使用Nacos Config作为ApiBoot RateLimiter的外部QPS配置方式,那么我们需要进行下面的步骤来完成:

添加依赖

  1. <!--Nacos-->
  2. <dependency>
  3. <groupId>com.alibaba.boot</groupId>
  4. <artifactId>nacos-config-spring-boot-starter</artifactId>
  5. </dependency>

ApiBoot内置了Nacos Starter的版本,这里无需添加版本号。

配置参数

我们添加了Nacos依赖,那需要进行配置Nacos Server的地址,具体Nacos Server怎么安装,可以去看我的博客文章或者直接访问Nacos 快速开始

Nacos在application.yml配置如下所示:

  1. # nacos config
  2. nacos:
  3. config:
  4. server-addr: 127.0.0.1:8848

8848Nacos Server的默认端口号,这里直接配置使用。

测试动态修改

我们在启动应用程序时,ApiBoot RateLimiter会自动从Nacos读取DATA_IDapiboot-rate-limiter-config的配置,分组为DEFAULT_GROUPProperties类型的配置文件,然后缓存到本地

如果Nacos并没有该配置文件,则在第一次访问接口时自动创建

ApiBoot RateLimiter - 图1

apiboot-rate-limiter-config配置文件是Properties形式的存储的,那么Key的生成规则则是把请求接口地址的/替换为了.,如下所示:

  1. .resource.=50
  2. .resource.detail=10

下面我们来测试修改配置后,ApiBoot RateLimiter是否可以实时生效,将.resource.修改为20后,控制台会打印如下日志信息:

  1. Update local current RateLimiter configuration is completecontent:{.resource.=20, .resource.detail=10}

当我们再次访问/resource/接口时就会发现限流的QPS一秒内只允许访问20次。