在Spring Boot应用中,接口防抖(Debounce)是一种常见的需求,特别是在处理高频触发的请求时,比如用户频繁点击按钮、自动补全搜索框等场景。防抖的目的是在一定时间内只执行一次操作,忽略其他重复的请求。
以下是几种实现接口防抖的方法:
1. 使用缓存
通过缓存机制来实现防抖。可以在第一次请求时缓存结果,并在指定时间内忽略后续的请求。
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
@Service
public class DebounceService {
@Cacheable(value = "debounceCache", key = "#key")
public String processRequest(String key) {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Processed: " + key;
}
@CachePut(value = "debounceCache", key = "#key")
public String updateCache(String key) {
return null; // 不返回任何值,仅更新缓存时间
}
}
在Controller中调用:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DebounceController {
@Autowired
private DebounceService debounceService;
@GetMapping("/debounce")
public String debounce(@RequestParam String key) {
// 先更新缓存,确保缓存时间是最新的
debounceService.updateCache(key);
// 处理请求
return debounceService.processRequest(key);
}
}
配置缓存过期时间:
yaml
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=100,expireAfterWrite=5s
2. 使用限流
虽然限流和防抖不完全相同,但可以通过限流来间接实现防抖的效果。
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.stereotype.Service;
@Service
public class RateLimiterService {
private final RateLimiter rateLimiter = RateLimiter.create(1.0); // 每秒最多处理1个请求
public String processRequest(String key) {
if (rateLimiter.tryAcquire()) {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Processed: " + key;
} else {
return "Request throttled";
}
}
}
在Controller中调用:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RateLimiterController {
@Autowired
private RateLimiterService rateLimiterService;
@GetMapping("/rateLimiter")
public String rateLimiter(@RequestParam String key) {
return rateLimiterService.processRequest(key);
}
}
3. 使用定时器
通过定时任务来实现防抖,确保在一定时间内只执行一次操作。
import org.springframework.stereotype.Service;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Service
public class DebounceExecutorService {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final ConcurrentHashMap<String, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
public void processRequest(String key, Runnable task) {
ScheduledFuture<?> scheduledTask = scheduledTasks.get(key);
if (scheduledTask != null) {
scheduledTask.cancel(false); // 取消之前的任务
}
scheduledTask = scheduler.schedule(task, 5, TimeUnit.SECONDS); // 5秒后执行
scheduledTasks.put(key, scheduledTask);
}
public void shutdown() {
scheduler.shutdown();
}
}
在Controller中调用:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DebounceExecutorController {
@Autowired
private DebounceExecutorService debounceExecutorService;
@GetMapping("/debounceExecutor")
public String debounceExecutor(@RequestParam String key) {
debounceExecutorService.processRequest(key, () -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Processed: " + key);
});
return "Request received, processing will start in 5 seconds if no new request arrives.";
}
}
4. 使用分布式缓存防抖
在分布式环境中,可以使用Redis来实现防抖。通过设置带有过期时间的键来确保在一定时间内只处理一次请求。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RedisDebounceService {
@Autowired
private StringRedisTemplate redisTemplate;
public String processRequest(String key) {
Boolean isNew = redisTemplate.opsForValue().setIfAbsent(key, "processing", 5, TimeUnit.SECONDS);
if (isNew != null && isNew) {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
redisTemplate.delete(key); // 处理完成后删除键
return "Processed: " + key;
} else {
return "Request throttled";
}
}
}
在Controller中调用:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RedisDebounceController {
@Autowired
private RedisDebounceService redisDebounceService;
@GetMapping("/redisDebounce")
public String redisDebounce(@RequestParam String key) {
return redisDebounceService.processRequest(key);
}
}
总结
- 缓存防抖:适用于单机环境,简单易实现。
- 限流:适用于需要控制请求速率的场景。
- 定时任务防抖:适用于单机环境,可以精确控制执行时间。
- Redis防抖:适用于分布式环境,确保多个实例之间的防抖一致性。