springcloudgateway RequestRateLimiter Redis 限流

前提,已集成springcloudgateway相关pom,如lettuce-core、spring-cloud-starter-gateway等基础pom

1、从限流需要的基础pom开始:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>

我使用的springboot版本:2.1.4.RELEASE      springcloud版本:Greenwich.RELEASE

2、config编写,这个一般网上搜索有3种,path、host、请求参数如:user

@Configuration
public class LimitingConfig {
    /**
     * 使用请求 IP 作为限流键
     *
     * @return key
     */

    @Bean(name = "hostKeyResolver")
    KeyResolver pathKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
}

3、配置文件(以properties为例,可自行转换yml)

server.port=9001
spring.application.name=limitService

# redis配置
spring.redis.host=127.0.0.1
spring.redis.password=redis
spring.redis.port=6379
spring.redis.database=0

#routes[id] id数值以自身项目为准

# 使用请求来源 IP 作为限流键
spring.cloud.gateway.routes[4].id=limitRouter
# 如果是用注册中心可以直接写应用名,如果不是可写url地址如 http://127.0.0.1:9002
spring.cloud.gateway.routes[4].uri=lb://limitService
# 请求路径为/api/limit/get
spring.cloud.gateway.routes[4].predicates[0]=Path=/api/limit/get
# 通过截取1层,实际访问的controller接口地址为/limit/get
spring.cloud.gateway.routes[4].filters[0]=StripPrefix=1
# 不可更改
spring.cloud.gateway.routes[4].filters[1].name=RequestRateLimiter
# 使用SpEL表达式从Spring容器中获取Bean对象
spring.cloud.gateway.routes[4].filters[1].args.key-resolver=#{@hostKeyResolver}
# 令牌桶每秒填充平均速率
spring.cloud.gateway.routes[4].filters[1].args.redis-rate-limiter.replenishRate=1
# 令牌桶的上限   突发周期为 burstCapacity(2) / replenishRate(1)秒,如果周期内有2/1次请求突发的情况,则第2/1次会有部分请求丢失,返回HTTP 429 - Too Many Requests
spring.cloud.gateway.routes[4].filters[1].args.redis-rate-limiter.burstCapacity=2

4、如果使用单机redis可使用压测工具直接压测访问 http://127.0.0.1:9001/api/limit/get 接口,如使用上例中配置,超过2个请求,后面的将会返回429状态码

5、如果使用的是阿里云集群,默认request_rate_limiter.lua执行存在问题,

--在debug情况下,会有以下2个错误
-- NOSCRIPT No matching script. Please use EVAL.
-- ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array, and KEYS should not be in expression

解决方法:需要在自身项目对应目录(META-INF/scripts/request_rate_limiter.lua,源码:GatewayRedisAutoConfiguration.java)下重写该文件,覆盖jar中的原始文件修改如下:

--踩坑参照 https://blog.csdn.net/qq_33996921/article/details/107204362
--错误
--NOSCRIPT No matching script. Please use EVAL.
--ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array, and KEYS should not be in expression
--注释掉 tokens_key、timestamp_key 在引用的地址直接填写数组


--local tokens_key = KEYS[1]
--local timestamp_key = KEYS[2]
--redis.log(redis.LOG_WARNING, "tokens_key " .. tokens_key)

local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])

local fill_time = capacity/rate
local ttl = math.floor(fill_time*2)

--redis.log(redis.LOG_WARNING, "rate " .. ARGV[1])
--redis.log(redis.LOG_WARNING, "capacity " .. ARGV[2])
--redis.log(redis.LOG_WARNING, "now " .. ARGV[3])
--redis.log(redis.LOG_WARNING, "requested " .. ARGV[4])
--redis.log(redis.LOG_WARNING, "filltime " .. fill_time)
--redis.log(redis.LOG_WARNING, "ttl " .. ttl)

local last_tokens = tonumber(redis.call("get", KEYS[1]))
if last_tokens == nil then
  last_tokens = capacity
end
--redis.log(redis.LOG_WARNING, "last_tokens " .. last_tokens)

local last_refreshed = tonumber(redis.call("get", KEYS[2]))
if last_refreshed == nil then
  last_refreshed = 0
end
--redis.log(redis.LOG_WARNING, "last_refreshed " .. last_refreshed)

local delta = math.max(0, now-last_refreshed)
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
local allowed_num = 0
if allowed then
  new_tokens = filled_tokens - requested
  allowed_num = 1
end

--redis.log(redis.LOG_WARNING, "delta " .. delta)
--redis.log(redis.LOG_WARNING, "filled_tokens " .. filled_tokens)
--redis.log(redis.LOG_WARNING, "allowed_num " .. allowed_num)
--redis.log(redis.LOG_WARNING, "new_tokens " .. new_tokens)

redis.call("setex", KEYS[1], ttl, new_tokens)
redis.call("setex", KEYS[2], ttl, now)

return { allowed_num, new_tokens }

参考资料:

https://blog.csdn.net/qq_33996921/article/details/107204362

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值