In this post, I am going to explain how can we use Bucket4j to rate limit the API based on the pricing plan.
Suppose, I have a product P1, which support following price plans.
a. FREE – 5 requests per hour
b. BASIC - 400 requests per hour
c. STANDARD – 1000 requests per hour
d. PREMIUM – 10000 requests per hour.
When a client subscribe to my product P1, we will assign an apiKey to the client based on the price plan. Whenever client send request to the api, he should pass this apiKey. This would help us identify the pricing plan linked with the API client.
Step 1: Define the Buckets for each pricing plan.
PricingPlan.java
package com.sample.app.enums;
import java.time.Duration;
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Refill;
public enum PricingPlan {
FREE(5), BASIC(400), STANDARD(1000), PREMIUM(10000);
private int bucketCapacity;
private PricingPlan(int bucketCapacity) {
this.bucketCapacity = bucketCapacity;
}
public static Bucket resolveBucketApiKey(String apiKey) {
if (apiKey == null || apiKey.isEmpty()) {
Bandwidth bandwidth = Bandwidth.classic(PricingPlan.FREE.bucketCapacity,
Refill.intervally(PricingPlan.FREE.bucketCapacity, Duration.ofHours(1)));
return Bucket.builder().addLimit(bandwidth).build();
}
if (apiKey.startsWith("BASIC-")) {
Bandwidth bandwidth = Bandwidth.classic(PricingPlan.BASIC.bucketCapacity,
Refill.intervally(PricingPlan.BASIC.bucketCapacity, Duration.ofHours(1)));
return Bucket.builder().addLimit(bandwidth).build();
}
if (apiKey.startsWith("STANDARD-")) {
Bandwidth bandwidth = Bandwidth.classic(PricingPlan.STANDARD.bucketCapacity,
Refill.intervally(PricingPlan.STANDARD.bucketCapacity, Duration.ofHours(1)));
return Bucket.builder().addLimit(bandwidth).build();
}
if (apiKey.startsWith("PREMIUM-")) {
Bandwidth bandwidth = Bandwidth.classic(PricingPlan.PREMIUM.bucketCapacity,
Refill.intervally(PricingPlan.PREMIUM.bucketCapacity, Duration.ofHours(1)));
return Bucket.builder().addLimit(bandwidth).build();
}
Bandwidth bandwidth = Bandwidth.classic(PricingPlan.FREE.bucketCapacity,
Refill.intervally(PricingPlan.FREE.bucketCapacity, Duration.ofHours(1)));
return Bucket.builder().addLimit(bandwidth).build();
}
}
Step 2: Define PricePlanDemo class.
PricePlanDemo.java
package com.sample.app;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import com.sample.app.enums.PricingPlan;
import io.github.bucket4j.Bucket;
public class PricePlanDemo {
private static final Map<String, Bucket> BUCKETS_MAP = new ConcurrentHashMap<>();
private static void getResource(String apiKey) {
Bucket bucket = null;
if (BUCKETS_MAP.containsKey(apiKey)) {
bucket = BUCKETS_MAP.get(apiKey);
} else {
bucket = PricingPlan.resolveBucketApiKey(apiKey);
BUCKETS_MAP.put(apiKey, bucket);
}
if (bucket.tryConsume(1)) {
System.out.println("Resource accessed....");
return;
}
System.err.println("Too Many Requests, all the tokens consumed");
}
public static void main(String[] args) throws InterruptedException {
while (true) {
TimeUnit.SECONDS.sleep(1);
getResource("FREE-12easdfgft");
}
}
}
Sample Output
No comments:
Post a Comment