Enterprise rate limiter for javaee web applications, based on rate-limiter-web-core.
We believe that rate limiting should be as simple as:
@Rate("10/s") // 10 permits per second for all methods in this class@Path("/api")
publicclassGreetingResource {
@Rate(permits=10, when="web.request.user.role = GUEST")
@GET@Path("/smile")
publicStringsmile() {
return":)";
}
@Rate(permits=1, when="jvm.memory.available < 1gb")
@GET@Path("/greet")
publicStringgreet(@QueryParam("who") Stringwho) {
return"Hello " + who;
}
}Please first read the rate-limiter-web-core documentation.
To add a dependency on rate-limiter-javaee using Maven, use the following:
<dependency>
<groupId>io.github.poshjosh</groupId>
<artifactId>rate-limiter-javaee</artifactId>
<version>0.8.0</version> </dependency>1. Implement RateLimitProperties
publicclassRateLimitPropertiesImplimplementsRateLimitProperties {
// If not using annotations, return an empty list@OverridepublicList<String> getResourcePackages() {
returnCollections.singletonList("com.myapp.web.rest");
}
// If not using properties, return an empty map@OverridepublicList<Rates> getRates() {
// Accept only 2 tasks per secondreturnCollections.singletonList(Rates.of("task_queue", Rate.ofSeconds(2)));
}
}2. Extend RateLimitingDynamicFeature
This way a rate limiter will be created an automatically applied based on rate limiter related properties and annotations.
@javax.ws.rs.ext.ProviderpublicclassDynamicFeatureImplextendsRateLimitingDynamicFeature {
@javax.inject.InjectpublicDynamicFeatureImpl(RateLimitPropertiesproperties) {
super(properties);
}
}At this point, your application is ready to enjoy the benefits of rate limiting.
3. Annotate classes and/or methods.
@Path("/api")
classMyResource {
// Only 10 calls per second for users in role GUEST@Rate("10/s")
@RateCondition("web.request.user.role = GUEST")
@GET@Path("/greet")
@Produces("text/plain")
Stringgreet(Stringname) {
return"Hello " + name;
}
}Configure rate limiting as described in the rate-limiter-web-core documentation.
When you configure rate limiting using properties, you could:
Rate limit a class from properties by using the class ID.
Rate limit a method from properties by using the method ID.
publicclassRateLimitPropertiesImplimplementsRateLimitProperties {
@OverridepublicList<Rates> getRates() {
List<Rates> ratesList = newArrayList<>();
// Rate limit a classStringclassId = RateId.of(MyResource.class);
ratesList.add(Rates.of(classId, Rate.ofMinutes(10)));
// Rate limit a methodStringmethodId = RateId.of(MyResource.class.getMethod("greet", String.class));
ratesList.add(Rates.of(methodId, Rate.ofMinutes(10)));
returnratesList;
}
}The expression language allows us to write expressive rate conditions, e.g:
@RateCondition("web.request.user.role = GUEST")
@RateCondition("jvm.memory.free < 1GB")
| format | example | description |
|---|---|---|
| LHS = RHS | web.request.header[X-RateLimit-Limit] != | true, when the X-RateLimit-Limit header exists |
| LHS[key] = val | web.request.parameter[limited] = true | true, when request parameter limited equals true |
| LHS = [A ⎢ B] | web.request.user.role = [GUEST ⎢ RESTRICTED] | true, when the user role is either GUEST or RESTRICTED |
| LHS[key] = [A ⎢ B] | web.request.cookie[name] = [val_0 ⎢ val_1] | true, when cookie named name is either val_0 or val_1 |
| LHS[key] = [A & B] | web.request.header[name] = [val_0 & val_1] | true, when header named name has both val_0 and val_1 as values |
A rich set of conditions may be expressed as detailed in the web specification.
Usually, you are provided with appropriate RateLimiters based on the annotations
and properties you specify. However, you could manually create and use RateLimiters.
classMyResource {
RateLimiterrateLimiter = RateLimiterRegistry.of(MyResource.class, "smile");
@Rate(id = "smile", permits = 2)
Stringsmile() {
return":)";
}
}This way you use the RateLimiter as you see fit.
Please read the annotation specs. It is concise.
Enjoy! 😉