为什么Guava RateLimiter不再限制每秒太大的许可?

时间:2018-10-02 15:04:43

标签: java limit guava rate-limiting

我正在使用一些HTTP库将某些数据上传到某些Web服务,并且需要限制每秒上传的数据量。使用的限制器为Guava RateLimiter,在第二个示例中限制了一些数据流之后。对我来说有两个区别:我正在向消费者提供一些InputStream,并且在read方法中使用aquire,一次只能提供一个字节。据我所知,没有大小不同的数据包会使事情变得更容易。另外,我的每秒许可数比示例要大。

对于较低的数字,事情似乎工作得很好,但是对于较高的数字,事情就不行了,而且只有一个数字,事情开始不再起作用。我在KiB / s方面进行了限制,直到1 * 1024 * 976为止,包括977都可以使用,但是一切开始失败,并且限制似乎不再适用。使用某些网络监视器可以很容易地看到这一点,第一个配置将上传速度限制为〜7-8 MBit / s,而后者则提高了60或更高,具体取决于传出接口的使用方式等。据我了解,上传的增加应该小得多,仅为1 KiB / s,因此一点也不明显。

我可以使用以下代码重现该问题:

import com.google.common.util.concurrent.RateLimiter;

public class Test
{
    public static void main(String[] args)
    {
        RateLimiter rateLimiter = RateLimiter.create(1 * 1024 * 976);
        RateLimiter msgLimiter  = RateLimiter.create(1);
        long        aquired     = 0L;

        while (true)
        {
            rateLimiter.acquire();
            ++aquired;

            if (msgLimiter.tryAcquire())
            {
                System.out.println(
                    String.format(  "Aquired: %d MBit/s",
                                    (aquired * 8) / (1024 * 1024)));
                aquired = 0;
            }
        }
    }
}

结果为976

Aquired: 0 MBit/s
Aquired: 7 MBit/s
Aquired: 7 MBit/s
Aquired: 7 MBit/s
Aquired: 7 MBit/s

结果为977

Aquired: 0 MBit/s
Aquired: 77 MBit/s
Aquired: 85 MBit/s
Aquired: 82 MBit/s
Aquired: 83 MBit/s

您知道为什么会这样吗?谢谢!

我已经读过有关Rate bursts等的RateLimiter的“缺点”,但是看不到这是否以及如何解释了我的问题。

使用TimedSemaphore不会发生此问题:

import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.concurrent.TimedSemaphore;

import com.google.common.util.concurrent.RateLimiter;

public class Test
{
    public static void main(String[] args) throws InterruptedException
    {
        int             rateLimit   = 1 * 1024 * 2000;
        //RateLimiter   rateLimiter = RateLimiter.create(rateLimit);
        TimedSemaphore  rateLimiter = new TimedSemaphore(1, TimeUnit.SECONDS, rateLimit);
        RateLimiter     msgLimiter  = RateLimiter.create(1);
        long            aquired     = 0L;

        while (true)
        {
            rateLimiter.acquire();
            ++aquired;

            if (msgLimiter.tryAcquire())
            {
                System.out.println(
                    String.format(  "Aquired: %d MBit/s",
                                    (aquired * 8) / (1024 * 1024)));
                aquired = 0;
            }
        }
    }
}

带有976的结果与以前相同,因此,更高的值更受关注:

Aquired: 0 MBit/s
Aquired: 15 MBit/s
Aquired: 15 MBit/s
Aquired: 15 MBit/s
Aquired: 15 MBit/s

1 个答案:

答案 0 :(得分:1)

似乎Guava RateLimiter每秒最多只能处理1,000,000个获取呼叫。如果您尝试每秒进行1,000,001次获取呼叫,则对aquire的呼叫将根本不会等待(aquire()的返回值始终为0.0)->没有任何限制。

因此,以下情况可以按预期工作:

RateLimiter.create(1000000l); //使用aquire() / aquire(1)

时可以使用

RateLimiter.create(100000000l); //使用aquire(100)或更高版本时将起作用。

假设从不逐字节接收典型的网络流量,但是在100-1000字节的块中,Guava RateLimiter的工作极限为每个字节100,000,000(〜762 MBit)至1,000,000,000(7.45 GBit / s)字节第二。

如果这些值足以满足您的项目需求,则可以坚持使用Guava RateLimiter。如果没有,我建议改用Apache TimedSemaphore。