Backend Development 10 min read

Master Rate Limiting in Spring Boot with Bucket4j: Token Bucket Guide

This tutorial explains how to implement API rate limiting in Spring Boot using the Bucket4j library, covering token‑bucket theory, Maven dependencies, Redis configuration, YAML rules, annotation‑based limits, custom responses, and conditional bypasses with complete code examples.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Rate Limiting in Spring Boot with Bucket4j: Token Bucket Guide

1. Introduction

Rate limiting is a strategy that restricts the number of API calls a client can make within a certain time window, protecting the service from accidental or malicious overuse. It is typically enforced by tracking IP addresses, API keys, or tokens. When a client exceeds the limit, you can either queue the request until the window resets or reject it with HTTP 429 (Too Many Requests).

This article introduces the open‑source component Bucket4j , which provides powerful rate‑limiting based on the token‑bucket algorithm. It works for standalone JVM applications as well as clustered environments and supports in‑memory or distributed caches via the JCache (JSR‑107) specification.

2. Practical Example

2.1 Environment Setup

Add the following Maven dependencies:

<code>&lt;dependency&gt;</code><code>  &lt;groupId&gt;com.giffing.bucket4j.spring.boot.starter&lt;/groupId&gt;</code><code>  &lt;artifactId&gt;bucket4j-spring-boot-starter&lt;/artifactId&gt;</code><code>  &lt;version&gt;0.12.7&lt;/version&gt;</code><code>&lt;/dependency&gt;</code><code>&lt;dependency&gt;</code><code>  &lt;groupId&gt;com.bucket4j&lt;/groupId&gt;</code><code>  &lt;artifactId&gt;bucket4j-redis&lt;/artifactId&gt;</code><code>  &lt;version&gt;8.10.1&lt;/version&gt;</code><code>&lt;/dependency&gt;</code><code>&lt;dependency&gt;</code><code>  &lt;groupId&gt;redis.clients&lt;/groupId&gt;</code><code>  &lt;artifactId&gt;jedis&lt;/artifactId&gt;</code><code>&lt;/dependency&gt;</code><code>&lt;dependency&gt;</code><code>  &lt;groupId&gt;io.micrometer&lt;/groupId&gt;</code><code>  &lt;artifactId&gt;micrometer-core&lt;/artifactId&gt;</code><code>&lt;/dependency&gt;</code>

Configure a JedisPool bean:

<code>@Bean</code><code>public JedisPool jedisPool(@Value("${spring.data.redis.port}") Integer port,</code><code>    @Value("${spring.data.redis.host}") String host,</code><code>    @Value("${spring.data.redis.password}") String password,</code><code>    @Value("${spring.data.redis.database}") Integer database) {</code><code>    // buildPoolConfig() should be implemented by you</code><code>    final JedisPoolConfig poolConfig = buildPoolConfig();</code><code>    return new JedisPool(poolConfig, host, port, 60000, password, database);</code><code>}</code>

With the environment ready, you can define the API and apply rate‑limiting rules.

2.2 Configuration‑File Based Limiting

Define a simple ProductController :

<code>@RestController</code><code>@RequestMapping("/products")</code><code>public class ProductController {</code><code>    @GetMapping("/{id}")</code><code>    public Product getProduct(@PathVariable Integer id) {</code><code>        return new Product(id, "商品 - " + id, BigDecimal.valueOf(new Random().nextDouble(1000)));</code><code>    }</code><code>}</code>

Add the following YAML configuration (under bucket4j ) to limit each id to 2 requests per 30 seconds:

<code>bucket4j:</code><code>  cache-to-use: redis-jedis</code><code>  filter-config-caching-enabled: true</code><code>  filters:</code><code>  - cache-name: product_cache_name</code><code>    id: product_filter</code><code>    url: /products/.*</code><code>    rate-limits:</code><code>      - cache-key: getParameter("id")</code><code>        bandwidths:</code><code>          - capacity: 2</code><code>            time: 30</code><code>            unit: seconds</code><code>            refill-speed: interval</code>

When the limit is exceeded, the default response is HTTP 429. You can customize the response body and content type:

<code>bucket4j:</code><code>  filters:</code><code>  - cache-name: product_cache_name</code><code>    http-content-type: 'application/json;charset=utf-8'</code><code>    http-response-body: '{"code": -1, "message": "请求太快了"}'</code>

2.3 Annotation‑Based Limiting

Use the @RateLimiting annotation (AOP) to intercept methods. Example configuration for a method‑level limit:

<code>bucket4j:</code><code>  methods:</code><code>  - name: storage_rate</code><code>    cache-name: storage_cache_name</code><code>    rate-limit:</code><code>      bandwidths:</code><code>        - capacity: 2</code><code>          time: 30</code><code>          unit: seconds</code><code>          refill-speed: interval</code>

Apply the annotation to a controller method:

<code>@RateLimiting(</code><code>    name = "storage_rate",</code><code>    cacheKey = "'storage-' + #id",</code><code>    skipCondition = "#name eq 'admin'",</code><code>    ratePerMethod = true,</code><code>    fallbackMethodName = "getStorageFallback"</code><code>)</code><code>@GetMapping("/{id}")</code><code>public R<Storage> getStorage(@PathVariable Integer id, String name) {</code><code>    return R.success(new Storage(id, "SP001 - " + id, new Random().nextInt(10000)));</code><code>}</code><code>public R<Storage> getStorageFallback(Integer id, String name) {</code><code>    return R.failure(String.format("请求id=%d,name=%s被限流", id, name));</code><code>}</code>

The skipCondition attribute lets you bypass the limit when the request meets a specific condition (e.g., name == "admin" ).

You can also place @RateLimiting on a class so that all its methods are limited, and use @IgnoreRateLimiting on individual methods to exclude them.

Images

JavaRedisSpring BootRate Limitingtoken bucketBucket4j
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.