Backend Development 25 min read

Comprehensive Guide to Rate Limiting in Microservice Architecture Using Dubbo, Spring Cloud, Guava, Sentinel, Redis, and a Custom Spring Boot Starter

This article explains why rate limiting is critical for microservice stability, compares implementations across Dubbo, Spring Cloud, and gateway layers, details common algorithms such as token bucket, leaky bucket, and sliding window, and provides step‑by‑step code examples for Guava, Sentinel, Redis‑Lua, and a reusable Spring Boot starter to enforce rate limits in Java back‑end services.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Comprehensive Guide to Rate Limiting in Microservice Architecture Using Dubbo, Spring Cloud, Guava, Sentinel, Redis, and a Custom Spring Boot Starter

Rate limiting is essential in a microservice architecture to prevent a single overloaded service from becoming a hidden avalanche factor that can exhaust JVM resources and cause request timeouts.

The article first outlines the need to choose a rate‑limiting solution based on the chosen technology stack—whether the system uses Dubbo, Spring Cloud, or a gateway layer—highlighting that different stacks may require different configurations.

It then introduces three widely used rate‑limiting algorithms: the token bucket (which uses tokens and a bucket to control request flow), the leaky bucket (which queues requests in a bucket and drops excess when full), and the sliding time window (which counts requests in a moving window to smooth traffic spikes).

For practical enforcement, the article proposes using AOP with custom annotations to intercept methods and apply limits.

Guava implementation :

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>23.0</version>
</dependency>
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TokenBucketLimiter { int value() default 50; }
@Aspect
@Component
public class GuavaLimiterAop {
    private final Map
rateLimiters = new ConcurrentHashMap<>();
    @Pointcut("@annotation(com.example.TokenBucketLimiter)")
    public void pointcut() {}
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint p) throws Throwable {
        Method m = ((MethodSignature) p.getSignature()).getMethod();
        TokenBucketLimiter ann = m.getAnnotation(TokenBucketLimiter.class);
        String key = m.getDeclaringClass().getName() + "." + m.getName();
        RateLimiter rl = rateLimiters.computeIfAbsent(key, k -> RateLimiter.create(ann.value()));
        if (rl.tryAcquire()) {
            return p.proceed();
        }
        HttpServletResponse resp = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        resp.setContentType("application/json;charset=UTF-8");
        resp.getWriter().write("{\"success\":false,\"msg\":\"Rate limited\"}");
        return null;
    }
}

Sentinel implementation :

<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-core</artifactId>
  <version>1.8.0</version>
</dependency>
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SentinelLimiter { String resourceName(); int limitCount() default 50; }
@Aspect
@Component
public class SentinelLimiterAop {
    @Pointcut("@annotation(com.example.SentinelLimiter)")
    public void pointcut() {}
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint p) throws Throwable {
        Method m = ((MethodSignature) p.getSignature()).getMethod();
        SentinelLimiter ann = m.getAnnotation(SentinelLimiter.class);
        FlowRule rule = new FlowRule();
        rule.setResource(ann.resourceName());
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(ann.limitCount());
        FlowRuleManager.loadRules(Collections.singletonList(rule));
        try (Entry entry = SphU.entry(ann.resourceName())) {
            return p.proceed();
        } catch (BlockException e) {
            return "Rate limited";
        }
    }
}

Redis + Lua implementation :

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLimitAnnotation {
    String key();
    String prefix() default "";
    int count();
    int period();
    LimitType limitType() default LimitType.CUSTOMER;
}
-- limit.lua
local key = "rate.limit:" .. KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then
    return 0
else
    redis.call('INCRBY', key, 1)
    redis.call('expire', key, 2)
    return current + 1
end
@Aspect
@Component
public class LimitRestAspect {
    @Autowired private RedisTemplate
redisTemplate;
    @Autowired private DefaultRedisScript
redisScript;
    @Pointcut("@annotation(com.example.RedisLimitAnnotation)")
    public void pointcut() {}
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint p) throws Throwable {
        Method m = ((MethodSignature) p.getSignature()).getMethod();
        RedisLimitAnnotation ann = m.getAnnotation(RedisLimitAnnotation.class);
        String key = /* build key from IP, class, method, ann.key() */;
        Number count = redisTemplate.execute(redisScript, Collections.singletonList(key), ann.count(), ann.period());
        if (count != null && count.intValue() <= ann.count()) {
            return p.proceed();
        }
        throw new RuntimeException("Rate limited");
    }
}

The article also shows how to package these annotations and AOP classes into a reusable Spring Boot starter, placing them under annotation and aop packages, declaring them in spring.factories , building the project as a JAR, and then importing the starter in other services to obtain out‑of‑the‑box rate limiting.

In conclusion, the guide demonstrates that proper rate limiting—whether via token bucket, leaky bucket, sliding window, or framework‑specific tools like Guava, Sentinel, or Redis—protects microservice stability and can be modularized for reuse across multiple backend projects.

backendMicroservicesRedisSentinelGuavaRate Limitingspring boot starter
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

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.