使用java和redis而不锁定的速率限制器
我试图了解限速器是如何工作的 问题陈述是:每个IP地址每秒10个请求 Soln:我在博客中看到的是:使用java和redis而不锁定的速率限制器,java,architecture,redis,system-design,Java,Architecture,Redis,System Design,我试图了解限速器是如何工作的 问题陈述是:每个IP地址每秒10个请求 Soln:我在博客中看到的是: public void makeApiCall(String ip){ Long currentTime=Timestamp timestamp = System.currentTimeMillis(); String key=ip+":"+currentTime; Integer count=redisClient.get(key); if(count!=
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();
}
}
到现在为止,我忽略了删除旧钥匙。
我无法理解这个soln将如何工作?
根据我的说法,上述代码将在一秒钟内在多线程环境中进行10次以上的api调用。只能通过将redisClient.get(键)同步到callApi()代码来解决此问题
我已经把这个代码从
有谁能帮助我理解(通过修改上面的代码)在这些场景中使用redis的正确方法是什么
假设在当前秒中,9个请求已被服务。现在同时出现5个新请求,所有这些新线程在这5个线程执行“else”之前调用redisClient.get(key)因此,对于每个线程,计数将为9,否则将执行block,调用incr的次数将增加5倍,对于每个线程,将调用api。照目前的情况,代码确实容易受到竞争条件的影响(由于忽略了过期,内存会膨胀)。基本上有两种方法可以解决这个问题:一种是编译,另一种是编译Lua脚本 假设您使用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时,您不需要监视更改,因为整个脚本是原子的
最后,您在文档中提到的计数器模式似乎缺少了
WATCH
这个东西-我已经提交了一份PR来纠正它() 就目前而言,代码确实容易受到竞争条件的影响(并且由于忽略了过期,内存会膨胀)。基本上有两种方法可以解决这个问题:一种是编译,另一种是编译Lua脚本
假设您使用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时,您不需要监视更改,因为整个脚本是原子的
最后,您在文档中提到的计数器模式似乎缺少了
WATCH
这个东西-我已经提交了一份PR来纠正它() 您可以使用与Redis java客户端绑定的RRateLimiter
对象
RRateLimiter limiter = redisson.getRateLimiter("rate-limiter:" + ip);
// Initialization required only once.
// 5 permits per 2 seconds
limiter.trySetRate(RateType.OVERALL, 5, 2, RateIntervalUnit.SECONDS);
// acquire 3 permits or block until they became available
limiter.acquire(3);
您可以使用Redis java客户端绑定的
RRateLimiter
对象
RRateLimiter limiter = redisson.getRateLimiter("rate-limiter:" + ip);
// Initialization required only once.
// 5 permits per 2 seconds
limiter.trySetRate(RateType.OVERALL, 5, 2, RateIntervalUnit.SECONDS);
// acquire 3 permits or block until they became available
limiter.acquire(3);
您在上面提供的代码(尽管缺少EXPIRE)将限制为在一毫秒内调用10次
callApi()。您的问题是什么?我已经更新了问题。您在上面提供的代码(尽管没有过期)将限制为在一毫秒内调用10次callApi()。你的问题是什么?我已经更新了问题。