Ratelimiter With Resilience4j Spring Boot2

Page content

Introduction


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.

Version Details


  • spring-boot:2.4.3
  • Resilience4j:1.7.0
  • Java:11
  • Kotlin:1.3.71

Dependencies


We need to add the following dependencies in the pom.xml file -

  • resilience4j-spring-boot2
  • spring-boot-starter-aop

Configure rate limiter in the application.yml 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 propertyDefault valueDescription
timeoutDuration5 [s]The default wait time a thread waits for a permission
limitRefreshPeriod500 [ns]The period of a limit refresh. After each period the rate limiter sets its permissions to count back to the limitForPeriod value
limitForPeriod50The number of permissions available during one limit refresh period

Add rate limiter to the service


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}`")
}

Create controller class


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}`")
}

Run Application


Let’s check the status of the available permission for this service.

health status

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

Rate limiter status

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`

Source Code


The full source code is available at GitHub

References