Spring Boot RestController如何限制POST接口单线程执行其余返回429
问题根因
你当前代码的并发问题核心是isProcessAlive()状态判断和execute()执行两个操作不具备原子性,高并发场景下多个线程会同时通过isProcessAlive() == false的校验,全部进入执行逻辑,因此都返回201状态码。
解决方案
你可以用AtomicBoolean的原子CAS操作实现无锁的状态校验,保证同一时间只有一个线程能进入执行逻辑,其余线程直接返回429。
代码修改
首先在Controller类中定义一个原子布尔类型的全局执行标记:
// 类成员变量,默认false代表当前无任务在执行 private final AtomicBoolean taskRunning = new AtomicBoolean(false);
然后修改接口逻辑:
@ResponseStatus(code = HttpStatus.CREATED) @PostMapping(value ="/myApp",consumes="application/json",produces="application/json") public Execution execute(@RequestBody ParameterDTO StartDateParameter) { if(StartDateParameter.getStartDate()==null) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST); } // 原子操作:仅当标记为false时修改为true,返回true代表拿到执行权限 if (!taskRunning.compareAndSet(false, true)) { throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS); } try { // 可选:保留原有状态判断做二次防护 if(Executer.isProcessAlive()) { throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS); } return Executer.execute(StartDateParameter); } finally { // 无论执行成功/报错,最终都要重置标记,避免后续请求一直被拦截 taskRunning.set(false); } }
注意事项
- 如果
Executer.execute()是异步执行逻辑,不要在接口方法返回时就重置标记,需要等异步任务实际执行完成后再修改taskRunning的值,否则异步任务还在运行就释放了标记,还是会出现并发问题。 - 以上方案仅适用于单机部署场景,如果你的服务是集群部署,需要替换为分布式锁实现全局的执行状态校验。
- 不推荐使用
synchronized关键字包裹逻辑,该方案会导致后续请求阻塞等待,而非直接返回429,不符合你的需求。
内容的提问来源于stack exchange,提问作者user14857007
相关产品推荐
相关产品推荐

