Почему Guava RateLimiter больше не ограничивает слишком большие разрешения в секунду? - PullRequest
0 голосов
/ 02 октября 2018

Я загружаю некоторые данные, используя HTTP-библиотеку, в какой-то веб-сервис, и мне нужно ограничить количество загружаемых данных в секунду.Используется лимитер Guava RateLimiter , в значительной степени следуя второму примеру, ограничивающему некоторый поток данных.Два отличия для меня: я предоставляю немного InputStream потребителю и использую aquire в методе read , предоставляя только один байт за раз.Никаких пакетов разного размера или такого, которые, по моему мнению, должны облегчить задачу.Кроме того, мои разрешения в секунду больше, чем в примере.

Вещи, казалось, работали довольно хорошо для низких чисел, но не для более высоких, и есть ровно одно число, при котором вещи больше не работают.Я ограничиваю в терминах КиБ / с, и все, до 1 * 1024 * 976 включительно, работает, но с 977 вещи начинают терпеть неудачу, и ограничение, кажется, больше не применяется.Это легко увидеть, используя какой-то сетевой монитор: первая конфигурация ограничивает загрузку ~ 7-8 Мбит / с, а последняя увеличивает до 60 или более, в зависимости от того, как используется исходящий интерфейс, и так далее.Насколько я понимаю, увеличение загрузки должно быть намного меньше, всего 1 КиБ / с и поэтому вообще не заметно.

Я могу воспроизвести проблему, используя следующий код:

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

У вас есть идеи, почему это происходит?Спасибо!

Я уже читал о "недостатках" 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 голосов
/ 03 октября 2018

Кажется, что Guava RateLimiter может работать только до предела в 1 000 000 вызовов aquire в секунду.Если вы попытаетесь выполнить 1000 001 вызовов aquire в секунду, вызов aquire вообще не будет ждать (возвращаемое значение aquire() всегда равно 0.0) -> регулирование не происходит.

Таким образом, следующие сценарии работают должным образом:

RateLimiter.create(1000000l); // это будет работать при использовании aquire() / aquire(1)

RateLimiter.create(100000000l); // это будетработать при использовании aquire(100) или выше.

Предполагая, что типичный сетевой трафик никогда не принимается побайтово, но в блоках по 100-1000 байтов Guava RateLimiter будет работать до предела 100 000 000 (~ 762MBit) до 1 000 000 000 (7,45 Гбит / с) байт в секунду.

Если этих значений будет достаточно для вашего проекта, вы можете использовать Guava RateLimiter.Если нет, я бы рекомендовал использовать Apache TimedSemaphore.

...