Spring Boot:如何取消API请求及终止指定用户的请求执行
解决方案:维护请求状态并终止指定API请求
核心思路
要实现指定请求的终止,核心是给每个请求分配唯一标识,维护请求与执行任务的映射关系,同时让任务逻辑支持中断/取消响应。
具体实现步骤
1. 给每个请求分配唯一标识
客户端调用/run/{timeToRun}接口时,服务端生成唯一请求ID(如UUID)并返回给客户端,后续终止请求时需携带该ID。
2. 维护请求-任务映射关系
使用线程安全的ConcurrentHashMap存储请求ID与异步任务的映射,确保多线程环境下的安全访问。
3. 异步执行任务并支持取消
将原同步执行的逻辑改为异步任务(通过@Async实现),返回CompletableFuture对象,利用其cancel()方法终止任务。同时在循环逻辑中增加中断检查,确保任务能及时响应终止指令。
4. 提供终止请求的接口
新增一个终止接口,接收请求ID,从映射表中取出对应任务并执行取消操作。
代码示例
配置异步线程池(可选但推荐)
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setQueueCapacity(50); executor.setThreadNamePrefix("QueryExecutor-"); executor.initialize(); return executor; } }
改造Controller和任务逻辑
@RestController public class QueryController { // 线程安全的映射表:请求ID -> 异步任务 private final ConcurrentHashMap<String, CompletableFuture<Void>> taskMap = new ConcurrentHashMap<>(); @PostMapping("/run/{timeToRun}") public ResponseEntity<String> runQuery(@PathVariable Integer timeToRun) { String requestId = UUID.randomUUID().toString(); // 提交异步任务并存储到映射表 CompletableFuture<Void> future = queryService.executeQuery(timeToRun); taskMap.put(requestId, future); // 任务完成后自动从映射表移除,避免内存泄漏 future.whenComplete((v, e) -> taskMap.remove(requestId)); return ResponseEntity.ok(requestId); } @DeleteMapping("/stop/{requestId}") public ResponseEntity<String> stopQuery(@PathVariable String requestId) { CompletableFuture<Void> future = taskMap.get(requestId); if (future == null) { return ResponseEntity.badRequest().body("请求不存在或已完成"); } // 取消任务,参数true表示中断正在执行的线程 boolean isCancelled = future.cancel(true); if (isCancelled) { taskMap.remove(requestId); return ResponseEntity.ok("请求已终止"); } else { return ResponseEntity.ok("请求已完成或无法终止"); } } } @Service public class QueryService { @Async public CompletableFuture<Void> executeQuery(Integer timeToRun) { try { for (int i = 0; i < timeToRun; i++) { // 检查当前线程是否被中断,若中断则退出循环 if (Thread.currentThread().isInterrupted()) { throw new InterruptedException("任务被终止"); } // 执行业务逻辑 // do some logic // 模拟耗时操作 Thread.sleep(1000); } } catch (InterruptedException e) { // 恢复中断状态,确保上层能感知到中断 Thread.currentThread().interrupt(); } return CompletableFuture.completedFuture(null); } }
关键细节说明
- 线程安全:使用
ConcurrentHashMap保证多线程下对任务映射表的安全操作。 - 内存泄漏避免:通过
future.whenComplete()在任务完成后自动移除映射表中的条目。 - 中断响应:在循环中定期检查线程中断状态,确保任务能及时终止,而非执行到循环结束。
- 参数修正:原代码中
timeToRun为String类型,直接与int比较会报错,改为Integer类型更合理;同时修正了原@PostMapping路径中的多余}。
客户端操作流程
- 客户端调用
POST /run/{timeToRun},获取服务端返回的requestId。 - 若需要终止请求,调用
DELETE /stop/{requestId},携带之前获取的requestId。 - 服务端根据
requestId找到对应任务并终止。
内容的提问来源于stack exchange,提问作者Marcin_S
相关产品推荐
相关产品推荐

