令牌桶算法
原理
令牌桶算法是一种常用的流量控制算法,也可以用于接口防抖。其核心思想是系统以固定的速率向一个“令牌桶”中放入令牌,每个请求需要从令牌桶中获取一个或多个令牌才能被处理。如果令牌桶中没有足够的令牌,请求将被拒绝或等待。
代码示例(Java)
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class TokenBucket {
private final int capacity; // 令牌桶容量
private final int rate; // 令牌生成速率(每秒生成的令牌数)
private int tokens; // 当前令牌数量
private long lastRefillTime; // 上次填充令牌的时间
public TokenBucket(int capacity, int rate) {
this.capacity = capacity;
this.rate = rate;
this.tokens = capacity;
this.lastRefillTime = System.currentTimeMillis();
// 定时填充令牌
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(this::refill, 1, 1, TimeUnit.SECONDS);
}
// 填充令牌
private synchronized void refill() {
long now = System.currentTimeMillis();
int newTokens = (int) ((now - lastRefillTime) / 1000 * rate);
tokens = Math.min(capacity, tokens + newTokens);
lastRefillTime = now;
}
// 尝试获取令牌
public synchronized boolean tryAcquire() {
refill();
if (tokens > 0) {
tokens--;
return true;
}
return false;
}
public static void main(String[] args) {
TokenBucket tokenBucket = new TokenBucket(100, 10);
if (tokenBucket.tryAcquire()) {
System.out.println("请求被处理");
} else {
System.out.println("请求被拒绝,令牌不足");
}
}
}
代码解释
capacity
表示令牌桶的最大容量,rate
表示每秒生成的令牌数。refill
方法用于定时填充令牌,根据时间差计算需要填充的令牌数量,并更新当前令牌数量。tryAcquire
方法用于尝试获取令牌,如果令牌桶中有足够的令牌,则减少一个令牌并返回true
;否则返回false
。
信号量机制
原理
信号量是一种并发控制机制,它维护了一个计数器,表示可用的资源数量。每个请求需要获取一个信号量才能继续执行,如果信号量计数器为 0,则请求需要等待或被拒绝。
代码示例(Java)
import java.util.concurrent.Semaphore;
public class SemaphoreDebounce {
private final Semaphore semaphore;
public SemaphoreDebounce(int permits) {
this.semaphore = new Semaphore(permits);
}
public boolean canExecute() {
return semaphore.tryAcquire();
}
public void release() {
semaphore.release();
}
public static void main(String[] args) {
SemaphoreDebounce debounce = new SemaphoreDebounce(1);
if (debounce.canExecute()) {
try {
System.out.println("执行接口逻辑");
} finally {
debounce.release();
}
} else {
System.out.println("短时间内重复请求,不执行接口逻辑");
}
}
}
代码解释
Semaphore
类用于实现信号量机制,构造函数中的permits
参数表示可用的信号量数量。canExecute
方法调用tryAcquire
方法尝试获取一个信号量,如果获取成功则返回true
,否则返回false
。release
方法用于释放一个信号量,使其他请求可以继续获取。
分布式锁
原理
在分布式系统中,可以使用分布式锁来实现接口防抖。当一个请求到达时,尝试获取分布式锁,如果获取成功,则执行接口逻辑;如果获取失败,则说明有其他请求正在处理,当前请求被拒绝。
代码示例(Redis 分布式锁)
import redis.clients.jedis.Jedis;
public class RedisDistributedLock {
private static final String LOCK_KEY = "api_debounce_lock";
private static final int LOCK_EXPIRE_TIME = 500; // 锁的过期时间,单位:毫秒
private final Jedis jedis;
public RedisDistributedLock() {
this.jedis = new Jedis("localhost", 6379);
}
public boolean tryLock() {
String result = jedis.set(LOCK_KEY, "locked", "NX", "PX", LOCK_EXPIRE_TIME);
return "OK".equals(result);
}
public void unlock() {
jedis.del(LOCK_KEY);
}
public static void main(String[] args) {
RedisDistributedLock lock = new RedisDistributedLock();
if (lock.tryLock()) {
try {
System.out.println("执行接口逻辑");
} finally {
lock.unlock();
}
} else {
System.out.println("短时间内重复请求,不执行接口逻辑");
}
}
}
代码解释
- 使用 Redis 的
SET
命令并设置NX
(仅在键不存在时设置)和PX
(设置过期时间)选项来实现分布式锁。 tryLock
方法尝试获取锁,如果返回"OK"
则表示获取成功;否则表示锁已被其他请求占用。unlock
方法用于释放锁,通过删除 Redis 中的键来实现。
这些方法都可以有效地实现后端接口防抖,具体选择哪种方法取决于实际的业务需求和系统环境。