Implementing Rate Limiting in a Spring Boot API using Bucket4j
Tags: bucket4j, springboot, java, ratelimiting
Published: 2024-04-14 05:30:01

In this blog, we’ll explore how to add rate limiting to a Spring Boot API using Bucket4j. Applying Rate limiting will help control the number of requests a client can make to your API within a certain period to protect server resources and ensure fair usage.

## Introduction for Bucket4j

Bucket4j is a Java rate-limiting library that is used to implement various algorithms like token buckets and leaky buckets for rate limiting. It is highly efficient and easy to integrate with any Java-based application, including Spring Boot.

## Let’s set up a Spring Boot Project

Create a spring boot app and add the below dependencies.

```
<!-- Bucket4j -->
<dependency>
    <groupId>com.github.vladimir-bukhtoyarov</groupId>
    <artifactId>bucket4j-core</artifactId>
    <version>3.1.0</version>
</dependency>
```

## Creating a Rate Limiting Service

```
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import io.github.bucket4j.Refill;

import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class RateLimitingService {

    private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();

    public boolean allowRequest(String apiKey) {
        Bucket bucket = buckets.computeIfAbsent(apiKey, this::createNewBucket);
        return bucket.tryConsume(1);
    }

    private Bucket createNewBucket(String apiKey) {
        Bandwidth limit = Bandwidth.classic(10, Refill.intervally(10, Duration.ofMinutes(1)));
        return Bucket4j.builder().addLimit(limit).build();
    }
}
```
_**Note: If Refill.intervally is not available you can use Refill.smooth_

In this **RateLimitingService** class:

- We use a **ConcurrentHashMap** to store a bucket for each API key.

- The **allowRequest** method checks if a request can be consumed from the bucket associated with the given API key.

- The **createNewBucket** method creates a new bucket with a rate limit of 10 requests per minute.

Now let’s create a RateLimitingController class to integrate ratelimiting on API.

```
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/rate-limiting")
public class RateLimitingController {

    private final RateLimitingService rateLimitingService;

    public RateLimitingController(RateLimitingService rateLimitingService) {
        this.rateLimitingService = rateLimitingService;
    }

    @GetMapping("/resource")
    public String getResource() {
        String apiKey = "test-api-key"; // Retrieve API key from request headers or JWT token
        if (rateLimitingService.allowRequest(apiKey)) {
            return "Resource api accessed successfully";
        } else {
            return "Rate limit exceeded. Please Try again later.";
        }
    }
}
```
In this RateLimitingController class:

- We inject the RateLimitingService into the controller.

- The getResource method checks if the request can be allowed based on the rate limit associated with the API key.

## Conclusion

In this tutorial, it’s shown how to use Bucket4j to build rate restrictions in a Spring Boot API. You can protect your APIs against misuse and guarantee equitable client usage by managing the volume of incoming requests. Token buckets and other algorithm-based rate constraints can be effectively and adaptably implemented using Bucket4j. You are welcome to alter the rate-limiting parameters to suit the needs of your particular application.

For more advanced scenarios, consider integrating Bucket4j with caching mechanisms like Redis for distributed rate limiting across multiple server instances.

## References

https://bucket4j.com/
https://github.com/bucket4j/bucket4j
https://github.com/bucket4j/bucket4j

## Code Reference

https://github.com/tharindu1998/rate-limiting-example