Spring Boot如何实现特定POST请求队列逐个poll串行处理
Spring Boot特定POST接口串行处理实现方案
核心思路:通过单线程线程池隔离目标接口的业务执行逻辑,其余接口默认走Spring Boot原生Tomcat请求线程池,完全保留原有并发处理能力。
具体实现步骤
1. 配置专属单线程执行器
给需要串行处理的接口单独定义一个核心线程数为1的线程池,线程池内部自带线程安全的任务队列,所有提交到该池的任务会严格按照提交顺序串行执行,无需自己手写队列、轮询逻辑。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Configuration public class BizExecutorConfig { @Bean(name = "serialPostExecutor") public ExecutorService serialPostExecutor() { // 固定1个工作线程,任务按提交顺序排队执行 return Executors.newSingleThreadExecutor(); } }
2. 接口层隔离执行逻辑
在需要串行处理的POST接口中,将业务处理逻辑提交到上述单线程执行器,其余接口保持原有写法不变,完全不影响并发能力。
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @RestController public class BizController { @Resource(name = "serialPostExecutor") private ExecutorService serialPostExecutor; // 需要串行执行的特定POST接口 @PostMapping("/your/target/api") public TargetResp targetApi(@RequestBody TargetReq req) throws Exception { // 业务逻辑提交到单线程池排队 Future<TargetResp> taskFuture = serialPostExecutor.submit(() -> { // 写该接口的实际业务逻辑 return processTargetBiz(req); }); // 等待当前任务执行完成后返回结果,如果不需要同步返回可以直接去掉这行,提前返回接收成功响应 return taskFuture.get(); } // 其余所有接口保持原有写法,走默认Tomcat线程池,并发能力不受任何影响 @PostMapping("/other/api1") public OtherResp api1(@RequestBody OtherReq req) { return processOtherBiz1(req); } @PostMapping("/other/api2") public OtherResp api2(@RequestBody OtherReq req) { return processOtherBiz2(req); } }
原有手写队列方案失效原因
- 未隔离消费线程:Spring MVC会为每个进入的请求分配独立的Tomcat工作线程,所有线程同时作为消费者poll队列,必然出现多线程并发取数的问题。
- 加sleep无法解决竞争:所有请求线程几乎同时进入接口,休眠相同时长后唤醒依然会同时竞争队列资源,没有从根源上把消费者数量降到1。
可选优化配置
限制队列长度避免OOM
默认单线程池用的是无界队列,请求量过大时可能出现内存溢出,可以手动创建线程池指定队列长度和拒绝策略:
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @Bean(name = "serialPostExecutor") public ExecutorService serialPostExecutor() { return new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(200), // 队列最多缓存200个待处理任务 // 拒绝策略按业务需求选择,示例为队列满后由提交请求的Tomcat线程直接执行 new ThreadPoolExecutor.CallerRunsPolicy() ); }
实现慢轮询效果
如果需要每个任务处理完后间隔固定时间再处理下一个,直接在任务逻辑末尾加休眠即可,该休眠只会阻塞专属单线程,不会影响其他接口的处理:
Future<TargetResp> taskFuture = serialPostExecutor.submit(() -> { TargetResp resp = processTargetBiz(req); // 单个任务处理完后间隔300ms再处理下一个,实现慢轮询 Thread.sleep(300); return resp; });
内容的提问来源于stack exchange,提问作者Imran
相关产品推荐
相关产品推荐

