Guia rápido do Guava RateLimiter
1. Visão geral
Neste artigo, veremos a classeRateLimiter da bibliotecaGuava.
A classeRateLimiter é uma construção que nos permite regular a taxa em que ocorre algum processamento. Se criarmos umRateLimiter com N licenças - significa que o processo pode emitir no máximo N licenças por segundo.
2. Dependência do Maven
Estaremos usando a biblioteca do Guava:
com.google.guava
guava
22.0
A versão mais recente pode ser encontradahere.
3. Criando e usandoRateLimiter
Digamos que queremoslimit the rate of execution of the doSomeLimitedOperation() to 2 times per second.
Podemos criar uma instânciaRateLimiter usando seu método de fábricacreate():
RateLimiter rateLimiter = RateLimiter.create(2);
Em seguida, para obter uma licença de execução deRateLimiter,, precisamos chamar o métodoacquire():
rateLimiter.acquire(1);
Para verificar se funciona, faremos duas chamadas subsequentes para o método regulado:
long startTime = ZonedDateTime.now().getSecond();
rateLimiter.acquire(1);
doSomeLimitedOperation();
rateLimiter.acquire(1);
doSomeLimitedOperation();
long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime;
Para simplificar nosso teste, vamos supor que o métododoSomeLimitedOperation() esteja sendo concluído imediatamente.
Nesse caso, ambas as invocações do métodoacquire() não devem ser bloqueadas e o tempo decorrido deve ser menor ou menor que um segundo - porque ambas as permissões podem ser adquiridas imediatamente:
assertThat(elapsedTimeSeconds <= 1);
Além disso, podemos adquirir todas as licenças em uma chamadaacquire():
@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);
}
Isso pode ser útil se, por exemplo, precisarmos enviar 100 bytes por segundo. Podemos enviar cem vezes um byte adquirindo uma permissão por vez. Por outro lado, podemos enviar todos os 100 bytes de uma só vez, adquirindo todas as 100 permissões em uma operação.
4. Aquisição de licenças de forma bloqueadora
Agora, vamos considerar um exemplo um pouco mais complexo.
Vamos criar umRateLimiter com 100 licenças. Então, vamos executar uma ação que precisa adquirir 1000 licenças. De acordo com a especificação deRateLimiter,, tal ação precisará de pelo menos 10 segundos para ser concluída, pois somos capazes de executar apenas 100 unidades de ação por segundo:
@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);
}
Observe como estamos usando o métodoacquire() aqui - este é um método de bloqueio e devemos ser cautelosos ao usá-lo. Quando o métodoacquire() é chamado, ele bloqueia o thread em execução até que uma permissão esteja disponível.
Calling the acquire() without an argument is the same as calling it with a one as an argument - tentará adquirir uma licença.
5. Aquisição de licenças com tempo limite
A APIRateLimiter também tem um métodoacquire() muito útil queaccepts a timeout and TimeUnit as arguments.
Chamar este método quando não houver licenças disponíveis fará com que ele espere pelo tempo especificado e, em seguida, expire - se não houver licenças disponíveis suficientes dentro detimeout.
Quando não há licenças disponíveis dentro do tempo limite fornecido, ele retornafalse. Se umacquire() for bem-sucedido, éreturns verdadeiro:
@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();
}
Criamos umRateLimiter com uma licença, então tentar adquirir duas licenças sempre fará com quetryAcquire() retornefalse.
6. Conclusão
Neste tutorial rápido, demos uma olhada na construçãoRateLimiter da bibliotecaGuava.
Aprendemos como usarRateLimtiter para limitar o número de licenças por segundo. Vimos como usar sua API de bloqueio e também usamos um tempo limite explícito para adquirir a permissão.
Como sempre, a implementação de todos esses exemplos e fragmentos de código pode ser encontrada emGitHub project - este é um projeto Maven, portanto, deve ser fácil de importar e executar como está.