Кратко ръководство за Guava RateLimiter

1. Общ преглед

В тази статия ще разгледаме класа RateLimiter от библиотеката на Гуава .

Класът RateLimiter е конструкция, която ни позволява да регулираме скоростта, с която се извършва някаква обработка. Ако създадем RateLimiter с N разрешения - това означава, че процесът може да издава най-много N разрешения в секунда.

2. Зависимост на Maven

Ще използваме библиотеката на Гуава:

 com.google.guava guava 29.0-jre 

Най-новата версия можете да намерите тук.

3. Създаване и използване на RateLimiter

Да кажем, че искаме да ограничим скоростта на изпълнение на doSomeLimitedOperation () до 2 пъти в секунда.

Можем да създадем екземпляр RateLimiter, използвайки неговия фабричен метод create () :

RateLimiter rateLimiter = RateLimiter.create(2);

След това, за да получим разрешение за изпълнение от RateLimiter, трябва да извикаме метода придобиване () :

rateLimiter.acquire(1);

За да проверим дали работи, ще направим 2 последващи извиквания към дроселирания метод:

long startTime = ZonedDateTime.now().getSecond(); rateLimiter.acquire(1); doSomeLimitedOperation(); rateLimiter.acquire(1); doSomeLimitedOperation(); long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime;

За да опростим нашето тестване, нека приемем, че методът doSomeLimitedOperation () завършва незабавно.

В такъв случай и двете извиквания на метода придобиване () не трябва да блокират и изминалото време трябва да бъде по-малко или под една секунда - тъй като и двете разрешения могат да бъдат получени незабавно:

assertThat(elapsedTimeSeconds <= 1);

Освен това можем да придобием всички разрешения в едно обаждане за придобиване () :

@Test public void givenLimitedResource_whenRequestOnce_thenShouldPermitWithoutBlocking() { // given RateLimiter rateLimiter = RateLimiter.create(100); // when long startTime = ZonedDateTime.now().getSecond(); rateLimiter.acquire(100); doSomeLimitedOperation(); long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime; // then assertThat(elapsedTimeSeconds <= 1); }

Това може да бъде полезно, ако например трябва да изпратим 100 байта в секунда. Можем да изпращаме по сто пъти по един байт, за да получаваме по едно разрешение. От друга страна, можем да изпратим всичките 100 байта наведнъж, придобивайки всичките 100 разрешителни за една операция.

4. Получаване на разрешения по блокиращ начин

Сега, нека разгледаме малко по-сложен пример.

Ще създадем RateLimiter със 100 разрешителни. След това ще изпълним действие, което трябва да получи 1000 разрешителни. Според спецификацията на RateLimiter, такова действие ще се нуждае от поне 10 секунди, за да завърши, защото ние можем да изпълним само 100 единици за действие в секунда:

@Test public void givenLimitedResource_whenUseRateLimiter_thenShouldLimitPermits() { // given RateLimiter rateLimiter = RateLimiter.create(100); // when long startTime = ZonedDateTime.now().getSecond(); IntStream.range(0, 1000).forEach(i -> { rateLimiter.acquire(); doSomeLimitedOperation(); }); long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime; // then assertThat(elapsedTimeSeconds >= 10); }

Обърнете внимание, как използваме метода придобиване () тук - това е блокиращ метод и трябва да бъдем предпазливи, когато го използваме. Когато методът pridobi () бъде извикан, той блокира изпълняващата нишка, докато се получи разрешение.

Извикването на придобиване () без аргумент е същото като извикването му с едно като аргумент - то ще се опита да получи едно разрешение.

5. Придобиване на разрешителни с изчакване

В RateLimiter API има също много полезен придобива () метод, който приема изчакване и TIMEUNIT като аргументи.

Извикването на този метод, когато няма налични разрешения, ще го накара да изчака определено време и след това да изтече - ако няма достатъчно налични разрешения в рамките на таймаута.

Когато няма налични разрешения в рамките на дадения таймаут, той връща false. Ако придобиването () успее, то връща true:

@Test public void givenLimitedResource_whenTryAcquire_shouldNotBlockIndefinitely() { // given RateLimiter rateLimiter = RateLimiter.create(1); // when rateLimiter.acquire(); boolean result = rateLimiter.tryAcquire(2, 10, TimeUnit.MILLISECONDS); // then assertThat(result).isFalse(); }

Създадохме RateLimiter с едно разрешение, така че опитът за придобиване на две разрешения винаги ще накара tryAcquire () да върне false.

6. Заключение

В този бърз урок разгледахме конструкцията RateLimiter от библиотеката на Гуава .

Научихме как да използваме RateLimtiter, за да ограничим броя на разрешенията в секунда. Видяхме как да използваме нейния API за блокиране и също така използвахме изрично време за изчакване, за да получим разрешението.

Както винаги, изпълнението на всички тези примери и кодови фрагменти може да бъде намерено в проекта GitHub - това е проект на Maven, така че трябва да е лесно да се импортира и да се изпълнява както е.