micronaut:
ratelimiting:
paths: (1)
- /api/**
Micronaut RateLimiter
Rate limiting support for Micronaut
Version:
1 Introduction
This project brings rate limiting support to Micronaut. The core module contains the common functionality and configuration. An implementation is required to have any rate limiting functionality.
To get started, you need to declare a dependency on one of the implementations. See the next section on installation.
To use a snapshot version of this library, check the documentation to use snapshots.
2 Installation
To start using rate limiters in Micronaut, simply express a dependency on one of the implementations that are available. See the documentation for your desired implementation for details.
3 Configuration
The core library provides configuration that applies to every implementation. By default when an implementation is on the classpath and enabled, rate limiting will apply to all APIs. You can limit the functionality to a subset of your controllers and methods through the following configuration.
1 | One or more Ant style paths can be supplied to denote what parts of your application should be rate limited. |
The presence of the @RateLimit annotation does not automatically allow an API to be being rate limited. All APIs must be in one of the paths supplied via configuration. |
4 Getting Started
Before you can get started controlling rate limits, there is one concept to understand and potentially an implementation to write. Rate limits are typically broken up into groups or "buckets". For example a rate limit may apply per user, or per organization, etc. There is no clear common default way of grouping the users of an API so that implementation is up to you. An interface BucketNameResolver is used to allow for custom bucket name resolution logic.
We have provided a simple implementation that rate limits by IP address. That implementation is disabled by default. To turn it on, set micronaut.ratelimiting.ip-address-resolver: true
in your configuration. This implementation is mostly to make getting started with rate limiting easier.
Once an implementation of BucketNameResolver, by default all requests will be rate limited using the defaults set by the rate limiting implementation.
5 Usage
Rate limiting will apply to any paths that have been configured (by default all paths). You can control the rate limiter configuration for any given controller or method as well as exclude any controller or method from being rate limited.
Rate Limit Configurations
Each implementation will provide a way to supply rate limiting configurations. To use a given configuration for a section of your API, simply add the @RateLimit annotation to the controller or method and supply the configuration name. For example:
package example;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.annotation.Controller;
import io.micronaut.ratelimiter.annotation.NoRateLimit;
import io.micronaut.ratelimiter.annotation.RateLimit;
@RateLimit("high") (1)
@Controller("/api") (2)
public class RateLimitController {
@NoRateLimit (3)
HttpResponse<?> someMethod() {
return HttpResponse.ok();
}
}
package example
import io.micronaut.http.HttpResponse
import io.micronaut.http.annotation.Controller
import io.micronaut.ratelimiter.annotation.NoRateLimit
import io.micronaut.ratelimiter.annotation.RateLimit
@RateLimit("high") (1)
@Controller("/api") (2)
class RateLimitController {
@NoRateLimit (3)
HttpResponse<?> someMethod() {
HttpResponse.ok()
}
}
package example
import io.micronaut.http.HttpResponse
import io.micronaut.http.annotation.Controller
import io.micronaut.ratelimiter.annotation.NoRateLimit
import io.micronaut.ratelimiter.annotation.RateLimit
@RateLimit("high") (1)
@Controller("/api") (2)
class RateLimitController {
@NoRateLimit (3)
fun someMethod() = HttpResponse.ok<Any>()
}
1 | The annotation is controlling which configuration will apply to this controller. The annotation can also be applied to individual methods for finer control |
2 | The controllers path matches one of the configured paths |
3 | The @NoRateLimit annotation is being used to denote this controller should not be rate limited, even though its path does match one of the configured paths |
The @NoRateLimit can also be used at the controller level:
package example;
import io.micronaut.http.annotation.Controller;
import io.micronaut.ratelimiter.annotation.NoRateLimit;
@NoRateLimit
@Controller("/api")
public class NoRateLimitController {
}
package example
import io.micronaut.http.annotation.Controller
import io.micronaut.ratelimiter.annotation.NoRateLimit
@NoRateLimit
@Controller("/api")
class NoRateLimitController {
}
package example
import io.micronaut.http.annotation.Controller
import io.micronaut.ratelimiter.annotation.NoRateLimit
@NoRateLimit
@Controller("/api")
class NoRateLimitController {
}
6 Resilience4j
An implementation of rate limiting for Micronaut has been created using Resilience4j.
To get started, simply express a dependency on this module:
runtime("io.micronaut.ratelimiter:micronaut-ratelimiter-resilience4j")
<dependency>
<groupId>io.micronaut.ratelimiter</groupId>
<artifactId>micronaut-ratelimiter-resilience4j</artifactId>
<scope>runtime</scope>
</dependency>
6.1 Configuration
Resilience4j only has support for a limited number of options, and those options are configurable through Micronaut configuration. Each grouping of options is referred to as a "configuration" and those configurations can be referenced in your code through the @RateLimiter annotation.
If the annotation is missing, then the default configuration will be applied. The default configuration can be supplied through the special name of "default" in your configuration. If that is missing then the defaults provided by the Resilience4j library will be used. For example:
resilience4j:
ratelimiter:
configurations:
default: (1)
timeout: 100ms
low: (2)
period: 10s
limit: 1
high: (3)
period: 1s
limit: 100
1 | The default configuration that will be used if no configuration is set via the annotation. Note that the missing values will inherit the defaults from Resilience4j. |
2 | A configuration called "low". This configuration only allows a single request every 10 seconds. |
3 | A configuration called "high". This configuration allows for 100 requests every second. |
The "low" and "high" configurations do not inherit from the default. The default configuration only applies when there is no specified configuration for a rate limited API. |
For a full reference of the resilience4j options, see the See configuration reference.
6.2 Error Response
When the Resilience4j rate limiter rejects a request, a RequestNotPermitted
exception will be emitted. This exception can be handled just like any other in Micronaut and a default implementation of ExceptionHandler comes with this library to turn the exception into the standard 429 response.
If the default implementation is not sufficient, you may override it by replacing RequestNotPermittedHandler with your own implementation.
7 Repository
You can find the source code of this project in this repository: