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")
public class GreetingResource {
@Rate(permits=10, when="web.request.user.role = GUEST")
@GET @Path("/smile")
public String smile() {
return ":)";
}
@Rate(permits=1, when="jvm.memory.available < 1gb")
@GET @Path("/greet")
public String greet(@QueryParam("who") String who) {
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
public class RateLimitPropertiesImpl implements RateLimitProperties {
// If not using annotations, return an empty list
@Override
public List<String> getResourcePackages() {
return Collections.singletonList("com.myapp.web.rest");
}
// If not using properties, return an empty map
@Override
public List<Rates> getRates() {
// Accept only 2 tasks per second
return Collections.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.Provider
public class DynamicFeatureImpl extends RateLimitingDynamicFeature {
@javax.inject.Inject
public DynamicFeatureImpl(RateLimitProperties properties) {
super(properties);
}
}At this point, your application is ready to enjoy the benefits of rate limiting.
3. Annotate classes and/or methods.
@Path("/api")
class MyResource {
// 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")
String greet(String name) {
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.
public class RateLimitPropertiesImpl implements RateLimitProperties {
@Override
public List<Rates> getRates() {
List<Rates> ratesList = new ArrayList<>();
// Rate limit a class
String classId = RateId.of(MyResource.class);
ratesList.add(Rates.of(classId, Rate.ofMinutes(10)));
// Rate limit a method
String methodId = RateId.of(MyResource.class.getMethod("greet", String.class));
ratesList.add(Rates.of(methodId, Rate.ofMinutes(10)));
return ratesList;
}
}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.
class MyResource {
RateLimiter rateLimiter = RateLimiterRegistry.of(MyResource.class, "smile");
@Rate(id = "smile", permits = 2)
String smile() {
return ":)";
}
}This way you use the RateLimiter as you see fit.
Please read the annotation specs. It is concise.
Enjoy! 😉