Ratelimiter With Resilience4j Spring Boot2

2020-06-28 spring-boot resilience4j ratelimiter example

Reading time: 3 Min(s)

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.2.6.RELEASE
  • spring-cloud:Hoxton.SR4
  • Resilience4j:1.1.0
  • Java:11
  • Kotlin:1.3.71

Dependencies

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

  • resilience4j-reactor
  • resilience4j-circuitbreaker
  • resilience4j-spring-boot2
  • spring-boot-starter-actuator
  • 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: 10
      limitRefreshPeriod: 1s
      timeoutDuration: 0

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 count back to the limitForPeriod value
limitForPeriod 50 The 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 shall add the @RateLimiter annotation, and pass the config name and fallback method name, that would be call in case of request denied by the rate limiter.

@RateLimiter(name="processService", fallbackMethod = "processFallback")
fun process(): Mono<String> {
  return Mono.just("Hello World ...")
}

The fallback method name is processFallback it should be in the same class and it should have 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

Before running the application check the status of the available permission for this service using actuator health endpoint -

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:ah what do you want ...

Rate limiter permission details

Rate limiter status

Now this service have zero permission; now call it again and see, the call should be rejected 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


Reference