Tuesday, 5 July 2022

Bucket4j: Rate limit by pricing plan

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



 

 

Previous                                                 Next                                                 Home

No comments:

Post a Comment