You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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路径中的多余}。

客户端操作流程

  1. 客户端调用POST /run/{timeToRun},获取服务端返回的requestId。
  2. 若需要终止请求,调用DELETE /stop/{requestId},携带之前获取的requestId。
  3. 服务端根据requestId找到对应任务并终止。

内容的提问来源于stack exchange,提问作者Marcin_S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 22:15:57