当前位置:  开发笔记 > 编程语言 > 正文

限制方法在N秒内调用M个请求

如何解决《限制方法在N秒内调用M个请求》经验,为你挑选了6个好方法。

我需要一个组件/类来限制某些方法的执行到N秒内的最大M次调用(或ms或nanos,无关紧要).

换句话说,我需要确保我的方法在N秒的滑动窗口中执行不超过M次.

如果您不知道现有的课程,请随时发布您的解决方案/想法如何实现这一点.



1> Michael Borg..:

我使用固定大小为M的时间戳的环形缓冲区.每次调用该方法时,检查最旧的条目,如果它在过去的时间内小于N秒,则执行并添加另一个条目,否则您将睡眠为时差.


这就是你使用java.util.concurrent中的DelayQueue的原因.它可以防止多个线程作用于同一条目的问题.
对于多线程情况,我认为令牌桶方法可能是更好的选择.
可爱.正是我需要的.快速尝试显示~10行实现此目的并且内存占用量最小.只需要考虑线程安全和传入请求的排队.

2> schnatterer..:

对我来说开箱即用的是Google Guava RateLimiter.

// Allow one request per second
private RateLimiter throttle = RateLimiter.create(1.0);

private void someMethod() {
    throttle.acquire();
    // Do something
}


我不推荐这个解决方案,因为Guava RateLimiter会阻塞线程,这将很容易耗尽线程池.
@kaviddiss如果你不想阻止那么使用`tryAquire()`
当前实施RateLimiter的问题(至少对我而言)是它不允许大于1秒的时间段,因此例如每分钟1的速率.
@John B据我所知,你可以使用RateLimiter.create(60.0)+ rateLimiter.acquire(60)通过RateLimiter每分钟获得1个​​请求
@radiantRazor Ratelimiter.create(1.0 / 60)和acquire()每分钟实现1次呼叫。

3> erickson..:

具体而言,您应该能够使用a实现此功能DelayQueue.使用M Delayed最初设置为零的实例初始化队列.当对方法的请求进入时,会出现take一个令牌,导致该方法阻塞,直到满足限制要求为止.当一个令牌被占用时,add一个新的令牌延迟到队列N.


实际上,在这个应用程序中,由于添加到堆中的新元素几乎总是堆中的最大元素(即具有最长延迟),因此通常需要每次添加一次比较.此外,如果算法正确实现,数组将永远不会增长,因为只有在获取一个元素后才添加一个元素.
我发现这也很有用,如果你不希望请求发生大爆发,保持大小M和延迟N按照少数毫秒的顺序相对较小.例如.M = 5,N = 20ms将提供250 /秒的键控脉冲突发,大小为5.

4> Kevin..:

阅读令牌桶算法.基本上,你有一个带有令牌的桶.每次执行该方法时,都会获取一个令牌.如果没有更多令牌,则阻止直到获得一个令牌.同时,有一些外部演员以固定的间隔补充令牌.

我不知道有一个库(或类似的东西).您可以将此逻辑写入代码或使用AspectJ添加行为.


如果你每秒向桶中添加5个令牌(或者每5秒钟5 - (5个剩余)而不是每1/5秒1个怎么办?
感谢您的建议,有趣的算法.但它不完全是我需要的.例如,我需要将执行限制为每秒5次调用.如果我使用令牌桶并且10个请求同时进入,前5个呼叫将占用所有可用令牌并暂时执行,而剩余的5个呼叫将以1/5秒的固定间隔执行.在这种情况下,我需要剩余的5次呼叫才能在1秒后通过单次突发执行.
@valery是的.(记得在M处盖上代币)

5> 小智..:

如果您需要在分布式系统上运行的基于Java的滑动窗口速率限制器,则可能需要查看https://github.com/mokies/ratelimitj项目。

Redis支持的配置,将IP请求限制为每分钟50个,如下所示:

import com.lambdaworks.redis.RedisClient;
import es.moki.ratelimitj.core.LimitRule;

RedisClient client = RedisClient.create("redis://localhost");
Set rules = Collections.singleton(LimitRule.of(1, TimeUnit.MINUTES, 50)); // 50 request per minute, per key
RedisRateLimit requestRateLimiter = new RedisRateLimit(client, rules);

boolean overLimit = requestRateLimiter.overLimit("ip:127.0.0.2");

有关Redis配置的更多详细信息,请参见https://github.com/mokies/ratelimitj/tree/master/ratelimitj-redis。



6> Duarte Menes..:

这取决于应用程序.

想象一下这样的情况:多个线程希望令牌执行某些全局速率限制操作不允许突发(即您希望每10秒限制10个操作,但您不希望在第一秒内发生10个操作然后保留停了9秒).

DelayedQueue有一个缺点:线程请求令牌的顺序可能不是它们获得请求的顺序.如果多个线程被阻塞等待令牌,则不清楚哪个线程将使用下一个可用令牌.在我看来,你甚至可以让线程永远等待.

一种解决方案是在两个连续动作之间具有最小时间间隔,并且按照它们所请求的相同顺序采取动作.

这是一个实现:

public class LeakyBucket {
    protected float maxRate;
    protected long minTime;
    //holds time of last action (past or future!)
    protected long lastSchedAction = System.currentTimeMillis();

    public LeakyBucket(float maxRate) throws Exception {
        if(maxRate <= 0.0f) {
            throw new Exception("Invalid rate");
        }
        this.maxRate = maxRate;
        this.minTime = (long)(1000.0f / maxRate);
    }

    public void consume() throws InterruptedException {
        long curTime = System.currentTimeMillis();
        long timeLeft;

        //calculate when can we do the action
        synchronized(this) {
            timeLeft = lastSchedAction + minTime - curTime;
            if(timeLeft > 0) {
                lastSchedAction += minTime;
            }
            else {
                lastSchedAction = curTime;
            }
        }

        //If needed, wait for our time
        if(timeLeft <= 0) {
            return;
        }
        else {
            Thread.sleep(timeLeft);
        }
    }
}

推荐阅读
我我檬檬我我186
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有