Interface Idempotency and Distributed Rate Limiting: Concepts, Algorithms, and Practical Implementations
The article discusses how to achieve interface idempotency using unique business identifiers or token mechanisms, then explores distributed rate limiting by outlining its dimensions, common algorithms such as token bucket and leaky bucket, and provides concrete implementation examples with Guava RateLimiter, Nginx, and Redis‑Lua scripts.
This article explains the importance of interface idempotency, describing how to guarantee that repeated requests produce the same result by using a unique business number or a token‑based locking mechanism, and then introduces distributed rate limiting, covering its various dimensions, common algorithms, and practical implementation approaches.
1. Interface Idempotency
Idempotency ensures that multiple identical requests have no side effects; a typical example is a payment operation that must not be charged twice if the client retries due to network errors.
The core idea is to use a unique business identifier to check whether the operation has already been performed, and in concurrent scenarios acquire a lock before proceeding.
1.1 Update operation idempotency
One method is to include a version number in the update statement and use it as a condition:
update set version = version +1 ,xxx=${xxx} where id =xxx and version = ${version};1.2 Token mechanism for update/insert
When no unique business ID exists, generate a token on the registration page, store it in a hidden field, and use the token to obtain a distributed lock before performing the insert. The lock is not released after success, allowing it to expire automatically.
2. Distributed Rate Limiting
Rate limiting can be controlled along several dimensions:
Time window (e.g., per second, per minute)
Resource limits (maximum requests or connections)
Black/white lists
Distributed environment (limits apply across all nodes)
Typical rules combine multiple dimensions, such as limiting each IP to 10 requests per second while also limiting total QPS per server.
2.1 Common algorithms
Token Bucket : Tokens are added to a bucket at a fixed rate; a request proceeds only if a token can be taken.
Leaky Bucket : Requests are placed in a bucket and drained at a constant rate; excess requests are dropped.
2.2 Implementation approaches
2.2.1 Guava RateLimiter (client‑side)
Add the Guava dependency:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>Example controller:
@RestController
@Slf4j
public class Controller {
// 2 tokens per second
RateLimiter limiter = RateLimiter.create(2.0);
@GetMapping("/tryAcquire")
public String tryAcquire(Integer count) {
if (limiter.tryAcquire(count)) {
log.info("success, rate={}", limiter.getRate());
return "success";
} else {
log.info("reject, rate={}", limiter.getRate());
return "fail";
}
}
@GetMapping("/tryAcquireWithTimeout")
public String tryAcquireWithTimeout(Integer count, Integer timeout) {
if (limiter.tryAcquire(count, timeout, TimeUnit.SECONDS)) {
log.info("success, rate={}", limiter.getRate());
return "success";
} else {
log.info("reject, rate={}", limiter.getRate());
return "fail";
}
}
@GetMapping("/acquire")
public String acquire(Integer count) {
limiter.acquire(count);
log.info("blocked until token available, rate={}", limiter.getRate());
return "success";
}
}2.2.2 Nginx rate limiting
Define a limit zone in nginx.conf:
limit_req_zone $binary_remote_addr zone=iplimit:20m rate=1r/s;Apply it to a location:
server {
server_name www.test.com;
location /access-limit/ {
proxy_pass http://127.0.0.1:8080/;
limit_req zone=iplimit burst=2 nodelay;
}
}2.2.3 Redis + Lua distributed limiter
Lua script ( rateLimiter.lua) stored in the classpath:
-- Get the key representing the method
local methodKey = KEYS[1]
local limit = tonumber(ARGV[1])
local count = tonumber(redis.call('get', methodKey) or "0")
if count + 1 > limit then
return false
else
redis.call('INCRBY', methodKey, 1)
redis.call('EXPIRE', methodKey, 1)
return true
endJava service that executes the script:
@Service
@Slf4j
public class AccessLimiter {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisScript<Boolean> rateLimitLua;
public void limitAccess(String key, Integer limit) {
boolean allowed = stringRedisTemplate.execute(
rateLimitLua,
Collections.singletonList(key),
limit.toString()
);
if (!allowed) {
log.error("Access blocked for key={}", key);
throw new RuntimeException("Your access is blocked");
}
}
}Configuration to load the script:
@Configuration
public class RedisConfiguration {
@Bean
public StringRedisTemplate redisTemplate(RedisConnectionFactory factory) {
return new StringRedisTemplate(factory);
}
@Bean
public RedisScript<Boolean> loadRedisScript() {
DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
script.setLocation(new ClassPathResource("rateLimiter.lua"));
script.setResultType(Boolean.class);
return script;
}
}3. Annotation‑based limiter
Define a custom annotation:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AccessLimiterAop {
int limit();
String methodKey() default "";
}Aspect that checks the limit before method execution:
@Aspect
@Component
@Slf4j
public class AccessLimiterAspect {
@Autowired
private AccessLimiter accessLimiter;
@Pointcut("@annotation(com.example.AccessLimiterAop)")
public void cut() {}
@Before("cut()")
public void before(JoinPoint joinPoint) {
MethodSignature sig = (MethodSignature) joinPoint.getSignature();
Method method = sig.getMethod();
AccessLimiterAop ann = method.getAnnotation(AccessLimiterAop.class);
if (ann == null) return;
String key = StringUtils.isEmpty(ann.methodKey()) ? method.getName() : ann.methodKey();
accessLimiter.limitAccess(key, ann.limit());
}
}Usage in a controller:
@RestController
@Slf4j
public class DemoController {
@GetMapping("/test")
@AccessLimiterAop(limit = 1)
public String test() {
return "success";
}
}The article concludes with additional resources, interview questions, and promotional links.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
