# How to Implement Rate Limiting Using Redisson in ContiNew Admin

> Implement declarative distributed rate limiting in ContiNew Admin using Redisson. Protect API endpoints with simple annotations, no manual Redis scripting needed.

- Repository: [OpenContiNew/continew-admin](https://github.com/continew-org/continew-admin)
- Tags: how-to-guide
- Published: 2026-02-27

---

**ContiNew Admin provides a declarative, distributed rate-limiting solution built on Redisson that allows developers to protect API endpoints using simple annotations without manual Redis scripting.**

The ContiNew Admin open-source project leverages the **ContiNew Starter RateLimiter** module to deliver robust traffic control across distributed deployments. By integrating Redisson—the feature-rich Redis Java client—the framework offers a **token-bucket algorithm** implementation that shares state across all application instances. This guide demonstrates how to configure and apply rate limiting using the actual source code from the `continew-org/continew-admin` repository.

## How the Redisson Rate Limiter Works

### Redisson RRateLimiter as the Distributed Engine

At the core of the implementation lies Redisson's `RRateLimiter`, which stores token buckets directly in Redis. This guarantees that every instance of your application shares the same limiter state, enabling true **distributed rate limiting** across clusters. When a request hits any node, the node queries the central Redis store to check token availability before permitting the method to proceed.

### Spring Boot Starter Integration

The `continew-starter-ratelimiter` dependency (declared in [`continew-common/pom.xml`](https://github.com/continew-org/continew-admin/blob/main/continew-common/pom.xml)) auto-registers a Spring Bean that manages `RRateLimiter` instances. For each unique `name` defined in the `@RateLimiter` annotation, the starter creates or retrieves a configured limiter with the specified `rate` and `interval` parameters. If no tokens remain, the starter throws a `RateLimitException` and translates it into a standard API error response.

## Prerequisites and Configuration

### Maven Dependency

Add the rate limiter starter to your project dependencies. In ContiNew Admin, this is already included in the common module:

```xml
<!-- continew-common/pom.xml -->
<dependency>
    <groupId>top.continew.starter</groupId>
    <artifactId>continew-starter-ratelimiter</artifactId>
</dependency>

```

This dependency pulls in Redisson client libraries and the annotation-driven rate limiting aspect.

### Redisson Client Configuration

Enable and configure Redisson via your Spring Boot YAML configuration. The starter respects standard `spring.data.redisson` properties:

```yaml

# application-dev.yml (excerpt)

spring:
  data:
    redisson:
      enabled: true          # Activate Redisson auto-configuration

      mode: SINGLE           # Deployment mode: SINGLE, CLUSTER, MASTER_SLAVE, etc.

```

The `mode` parameter supports various Redis deployment topologies, ensuring the rate limiter works correctly whether you run a single Redis node or a clustered environment.

## Implementing Rate Limits with @RateLimiter

### Basic Annotation Usage

Apply the `@RateLimiter` annotation to any Spring bean method—typically controller endpoints—to enforce traffic limits. The annotation maps directly to Redisson's token-bucket parameters:

- **`name`**: The logical limiter name used as the Redis key
- **`key`**: SpEL expression resolving the rate-limit key (e.g., per-user or per-IP)
- **`rate`**: Number of tokens allowed per interval
- **`interval` + `unit`**: Duration of the interval (e.g., 1 MINUTE)
- **`type`**: Optional `LimitType` (IP, USER) for additional granularity
- **`message`**: Error message returned when limit is exceeded

### Advanced Key Expressions

The `key` attribute accepts Spring Expression Language (SpEL) expressions, allowing dynamic resolution based on method arguments or context. In [`continew-server/src/main/java/top/continew/admin/controller/CaptchaController.java`](https://github.com/continew-org/continew-admin/blob/main/continew-server/src/main/java/top/continew/admin/controller/CaptchaController.java), the implementation combines email addresses with configuration properties to create distinct buckets:

```java
// CaptchaController.java excerpt
@GetMapping("/mail")
@RateLimiter(
    name = CacheConstants.CAPTCHA_KEY_PREFIX + "MIN",
    key = "#email + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.mail.templatePath')",
    rate = 2, 
    interval = 1, 
    unit = TimeUnit.MINUTES,
    message = "获取验证码操作太频繁，请稍后再试")
public R getMailCaptcha(@NotBlank @Email String email, CaptchaVO captchaReq) {
    // Business logic executes only if token is available
}

```

This configuration creates a unique limiter for each email-template combination, preventing abuse while allowing legitimate multi-template usage.

### Multiple Limits with @RateLimiters

For layered protection, use the container annotation `@RateLimiters` to apply several limits simultaneously. The following example from the captcha controller enforces both a strict per-minute limit and a broader per-hour limit:

```java
// CaptchaController.java excerpt
@Operation(summary = "获取邮箱验证码")
@GetMapping("/mail")
@RateLimiters({
    @RateLimiter(
        name = CacheConstants.CAPTCHA_KEY_PREFIX + "MIN",
        key = "#email + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.mail.templatePath')",
        rate = 2, interval = 1, unit = TimeUnit.MINUTES,
        message = "获取验证码操作太频繁，请稍后再试"),
    @RateLimiter(
        name = CacheConstants.CAPTCHA_KEY_PREFIX,
        key = "#email",
        rate = 30, interval = 1, unit = TimeUnit.MINUTES,
        type = LimitType.IP,
        message = "获取验证码操作太频繁，请稍后再试")
})
public R getMailCaptcha(String email, CaptchaVO captchaReq) {
    // Method protected by two independent rate limits
}

```

The first limiter restricts specific template requests, while the second applies a global IP-based cap across all captcha requests.

## Handling Rate Limit Exceptions

When token exhaustion occurs, the starter throws a `RateLimitException` containing the configured `message`. The framework automatically converts this to a standardized `R` response object returned to the client. To customize error handling globally, provide a `@ControllerAdvice` that catches `RateLimitException` and formats the response according to your API standards. No additional try-catch blocks are required in annotated methods.

## Summary

- **Distributed by design**: Redisson's `RRateLimiter` ensures consistent token accounting across all service instances via Redis.
- **Zero boilerplate**: The `continew-starter-ratelimiter` dependency handles Redisson client management and limiter lifecycle automatically.
- **Declarative syntax**: Use `@RateLimiter` or `@RateLimiters` annotations with SpEL expressions to define granular, context-aware limits.
- **Production ready**: Supports multiple Redis deployment modes (SINGLE, CLUSTER, MASTER_SLAVE) through standard Spring configuration properties.

## Frequently Asked Questions

### How does Redisson ensure rate limits work across multiple server instances?

Redisson stores the token bucket state in a centralized Redis database. Each application instance uses the `RRateLimiter` implementation to query and decrement tokens from this shared store, ensuring that the rate limit counts requests from all nodes collectively rather than per-instance.

### What is the difference between the `name` and `key` attributes in @RateLimiter?

The `name` attribute defines the logical limiter identifier used as the base Redis key structure, while the `key` attribute resolves to a dynamic suffix via SpEL expressions. For example, `name="captcha"` combined with `key="#userId"` creates unique limiters per user under the same logical grouping.

### Can I implement different rate limits for different user types?

Yes. Use SpEL expressions in the `key` attribute to differentiate limiters by user characteristics, or apply multiple `@RateLimiter` annotations via `@RateLimiters`. Combine this with the `type` parameter (LimitType.IP or LimitType.USER) to apply distinct policies based on IP address or authenticated user identity.

### What happens if the Redis connection fails?

If Redis becomes unavailable, Redisson will throw connection exceptions. The ContiNew Admin starter does not provide fallback mechanisms for rate limiting when Redis is down; the method invocation will fail fast rather than allowing unlimited requests. Ensure Redis high availability through clustering or Sentinel configurations to maintain rate limiting integrity.