当前位置: 首页 > 知识库问答 >
问题:

使用java和redis不加锁的速率限制器

茹康裕
2023-03-14

我想了解限速器是怎么工作的。

我在博客中看到的是:

public void makeApiCall(String ip){
    Long currentTime=Timestamp timestamp = System.currentTimeMillis();

    String key=ip+":"+currentTime;

    Integer count=redisClient.get(key);

    if(count!=null && count > 10){
         throw LimitExceededException();
    }
    else{
         redisClient.incr(key,1);
         callApi();
    }
}

到现在为止,我忽略了删除旧的钥匙。我不明白这个索伦是怎么工作的?正如上面所说,在多线程环境中,代码将在一秒钟内完成10个以上的api调用。它只能通过将redisclient.get(key)同步到callApi()代码来解决。

我从

假设在当前的第二个中已经服务了9个请求,现在同时出现了5个新的请求,所有这些新的线程在这5个线程中的任何一个执行“else”块之前调用redisclient.get(key)。因此,对于每个线程,计数将是9,else块将被执行,并且incr将被调用5次,对于每个线程,将调用api。

共有1个答案

边永贞
2023-03-14

实际上,代码确实容易受到竞争条件(和内存膨胀,因为您没有考虑过期)的影响。解决这一问题的方法基本上有两种:使用watchmulti/exec事务,或者使用Lua脚本的eval

假设您使用Jedis作为Java客户机,那么类似于下面这样的操作就可以处理事务:

public void makeApiCall(String ip){
    Long currentTime=Timestamp timestamp = System.currentTimeMillis();

    String key=ip+":"+currentTime;

    redisClient.watch(key);
    Integer count=redisClient.get(key);

    if(count!=null && count > 10){
         throw LimitExceededException();
    }
    else{
         Transaction t = redisClient.multi();
         t.incr(key,1);
         List<Object> resp = t.exec();
         if(resp != null){
             callApi();
         }
    }
}

Lua则是另一回事,但基本上假设您提供以下脚本(整个密钥名(ip+ts),只要您的代码在获得OK后继续使用callapi,它就会执行同样的操作:

local count = redis.call('GET', KEYS[1])
if count and tonumber(count) > 10 then
    return redis.error('count exceeded')
else
    redis.call('INCR', KEYS[1])
    redis.call('EXPIRE', KEYS[1], 10)
    return 'OK'
end

注意,使用Lua您不需要观察更改,因为整个脚本是原子的。

最后,您在文档中提到的计数器模式似乎缺少watch这一点--我已经提交了一个PR来纠正这一点(https://github.com/antirez/redis-doc/pull/888)。

 类似资料:
  • 我使用和来实施速率限制,例如每分钟5个请求: 然而,可以在最后一分钟发送5个请求,在第二分钟的第一秒发送5个请求,即在两秒钟内发送10个请求。 如何避免这个问题? 更新:我提出了这个清单。这是一个好方法吗?

  • 我需要限制每分钟的请求数。我还没有找到任何方法来使用Spring云网关redis。下面是当前的实现:图像实现 customRouter.getBurstCapacitycustomRouter.getReplenishRate 两者都仅在几秒钟内运行。示例:20 个请求在同一秒内与。 我在1分钟内需要20个请求。

  • 我不能玩春云之门的限速器。我在这里配置网关: 我使用JMeter调用API循环1000次,但网关没有响应429响应中的请求太多。我哪里错了?

  • 我正在使用PHP作为我的webservice的后端,我需要webservice能够设置一个速率限制,在过去24小时内,基于用户的PHP会话,最多1000个请求。这是不是可以做到这一点,而不使用数据库和只是速率限制,只使用PHP。我目前已经为每个会话每秒1个请求设置了速率限制器,但我希望在过去24小时内为每个会话设置1000个请求的速率限制。因为我是PHP的新手,任何帮助都会很好。 这里是我为每秒1

  • 我正在使用ProjectReactor使用rest从web服务加载数据。这是与多个线程并行完成的。我开始达到web服务的速率限制,因此我希望每秒最多发送10个请求,以避免出现这些错误。用Reactor我该怎么做? 使用zipWith(Mono.delayMillis(100))?还是有更好的办法? 非常感谢。

  • 速率限制配置参考 filter.http.RateLimit filter.http.RateLimit proto { "domain": "...", "stage": "...", "request_type": "...", "timeout": "{...}" } domain (string, REQUIRED) 需要调用速率限制服务时的域。 stage (uint3