Rate limiting is technique to help to limit the number of requests or type of request received by a server. It help to scale and increase the reliability of the system. As per resilience4j doc
Rate limiting is an imperative technique to prepare your API for scale and establish high availability and reliability of your service. But also, this technique comes with a whole bunch of different options of how to handle a detected limits surplus, or what type of requests you want to limit. You can simply decline this over limit request, or build a queue to execute them later or combine these two approaches in some way.
We need to add the following dependencies in the pom.xml file -
Open application.yml
and add the following configuration for the rate limiter
-
resilience4j.ratelimiter:
instances:
processService:
limitForPeriod: 1
limitRefreshPeriod: 15s
timeoutDuration: 1
registerHealthIndicator: true
The details of the configuration is as below -
Config property | Default value | Description |
---|---|---|
timeoutDuration | 5 [s] | The default wait time a thread waits for a permission |
limitRefreshPeriod | 500 [ns] | The period of a limit refresh. After each period the rate limiter sets its permissions to count back to the limitForPeriod value |
limitForPeriod | 50 | The number of permissions available during one limit refresh period |
I created a simple service that takes no arguments, and return some string mono. We will add the @RateLimiter
annotation, and pass the config name, fallback method name that gett calls in case of request denied by the rate limiter.
@RateLimiter(name="processService", fallbackMethod = "processFallback")
fun process(): Mono<String> {
return Mono.just("Hey there! what do you want ...")
}
The fallback method name is processFallback
. The method should be in the same class with the same signature but with an extra parameter for the Throwable
class for the exception handling.
So fallback method should look like this
fun processFallback(exp: Throwable): Mono<String> {
log.error("eh!!! this is the error ${exp.localizedMessage}")
return Mono.just("inside from fallback method because `${exp.localizedMessage}`")
}
The controller class should accept a get request and return the response Mono -
@GetMapping("/process", produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
fun processFallback(exp: Throwable): Mono<String> {
log.error("eh!!! this is the error ${exp.localizedMessage}")
return Mono.just("inside from fallback method because `${exp.localizedMessage}`")
}
Let’s check the status of the available permission for this service.
We have only one permission, now run the following script on the terminal
http :8080/process
Output
$ http :8080/process
HTTP/1.1 200 OK
Content-Type: text/event-stream;charset=UTF-8
transfer-encoding: chunked
data:Hey there! what do you want ...
Rate limiter permission details
Now, this service has zero permission; now call it again and see, the call should reject now -
http :8080/process
Output
$ http :8080/process
HTTP/1.1 200 OK
Content-Type: text/event-stream;charset=UTF-8
transfer-encoding: chunked
data:inside from fallback method because `RateLimiter 'processService' does not permit further calls`
The full source code is available at GitHub