比方说,我有 P 个进程在 N 个物理机器上运行一些业务逻辑。例如,这些进程调用一些 Web 服务 S。我想确保所有 P 进程相结合,每秒对服务 S 的调用不超过 X 次。
如何实施这样的解决方案?
Google Guava 的速率限制器适用于在单机上运行的进程,但不适用于分布式设置。
是否有任何可用于 JAVA 的标准、即用型解决方案?[可能基于zookeeper]
谢谢!
比方说,我有 P 个进程在 N 个物理机器上运行一些业务逻辑。例如,这些进程调用一些 Web 服务 S。我想确保所有 P 进程相结合,每秒对服务 S 的调用不超过 X 次。
如何实施这样的解决方案?
Google Guava 的速率限制器适用于在单机上运行的进程,但不适用于分布式设置。
是否有任何可用于 JAVA 的标准、即用型解决方案?[可能基于zookeeper]
谢谢!
我一直在为这类问题开发一个开源解决方案。
Limited 是限制的“服务器”。这些限制是使用令牌桶算法实现的。
基本上,您在服务配置中定义限制,如下所示:
buckets:
"request to service a":
per_minute: 10
"request to service b":
per_minute: 5
该服务作为在 TCP/IP 端口上侦听的守护程序运行。
然后您的应用程序会执行以下操作:
var limitd = new Limitd('limitd://my-limitd-address');
limitd.take('request to service a', 'app1' 1, function (err, result) {
if (result.conformant) {
console.log('everything is okay - this should be allowed');
} else {
console.error('too many calls to this thing');
}
});
我们目前正在使用它来限制某些应用程序事件的速率和去抖动。
服务器开启:
https://github.com/auth0/limitd
我们计划开发几个 SDK,但目前我们只有 node.js 并部分实现了 go:
因此,对于所有分布式速率限制架构,您将需要一个单一的后端存储来充当 true 的单一来源来跟踪请求的数量。为了方便起见,您始终可以将 zookeeper 用作内存数据存储,尽管有更好的选择,例如 Redis。
Java 中的简单速率限制,您希望每 3 秒实现 3 个事务的并发。如果你想集中它,那么要么将令牌数组存储在 elasticache 或任何数据库中。代替同步块,您还必须实现锁定标志。
import java.util.Date;
public class RateLimiter implements Runnable {
private long[] tokens = new long[3];
public static void main(String[] args) {
// TODO Auto-generated method stub
RateLimiter rateLimiter = new RateLimiter();
for (int i=0; i<20; i++) {
Thread thread = new Thread(rateLimiter,"Thread-"+i );
thread.start();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
long currentStartTime = System.currentTimeMillis();
while(true) {
if(System.currentTimeMillis() - currentStartTime > 100000 ) {
throw new RuntimeException("timed out");
}else {
if(getToken()) {
System.out.println(Thread.currentThread().getName() +
" at " +
new Date(System.currentTimeMillis()) + " says hello");
break;
}
}
}
}
synchronized private boolean getToken() {
// TODO Auto-generated method stub
for (int i = 0; i< 3; i++) {
if(tokens[i] == 0 || System.currentTimeMillis() - tokens[i] > 3000) {
tokens[i] = System.currentTimeMillis();
return true;
}
}
return false;
}
}
https://github.com/jdwyah/ratelimit-java提供了应该做到这一点的分布式速率限制。您可以将您的限制配置为 S 每秒 / 分钟等,并选择掩护下的漏桶的突发大小 / 重新填充率。