Spring MVC框架是否存在处理渐进式任务的设计模式?
Spring MVC框架是否存在处理渐进式任务的设计模式?
嘿,你的需求我太懂了——做导入任务的时候,用户盯着空白页面等半天,还不能取消,体验真的拉胯。其实Spring生态里已经有几个现成的方案,比你现在搭的WebSocket那套要省心不少,不用自己维护会话指针、处理各种消息类型的复杂逻辑。
我给你分场景推荐几个方案:
轻量场景:@Async + 事件驱动 + SSE
如果你的导入任务量级不算特别大(比如几万条以内),这个组合是最省心的:
- 用
@Async把导入任务异步执行,避免阻塞请求线程 - 结合Spring的事件机制(
ApplicationEventPublisher),在任务执行过程中发布进度更新、任务完成、任务失败、任务中断这几类事件 - 前端用**Server-Sent Events(SSE)**接收后端的状态推送(比WebSocket轻量,不用双向通信,单向推送进度完全够用),或者简单点用定时轮询接口查状态
- 中断功能的话,给每个任务生成唯一ID(比如UUID),用
ConcurrentHashMap把任务的Future对象存起来,前端发中断请求时,根据ID找到Future调用cancel(true)就行
给你贴个极简的代码片段参考:
// 异步配置类 @Configuration @EnableAsync public class AsyncTaskConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(3); executor.setMaxPoolSize(10); executor.setQueueCapacity(50); executor.initialize(); return executor; } } // 导入任务服务 @Service public class ImportService { private final ConcurrentHashMap<String, Future<?>> runningTasks = new ConcurrentHashMap<>(); private final ApplicationEventPublisher eventPublisher; public ImportService(ApplicationEventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } @Async public Future<?> startImport(String taskId, ImportRequest request) { try { int totalItems = countImportItems(request); for (int i = 0; i < totalItems; i++) { // 执行单条数据导入 importSingleItem(request.getItems().get(i)); // 发布进度事件 eventPublisher.publishEvent(new ImportProgressEvent(taskId, (i+1)*100/totalItems)); // 检查是否被中断 if (Thread.currentThread().isInterrupted()) { eventPublisher.publishEvent(new ImportInterruptedEvent(taskId)); return new AsyncResult<>(null); } } eventPublisher.publishEvent(new ImportCompletedEvent(taskId)); } catch (Exception e) { eventPublisher.publishEvent(new ImportFailedEvent(taskId, e.getMessage())); } finally { runningTasks.remove(taskId); } return new AsyncResult<>(null); } public void interruptTask(String taskId) { Future<?> task = runningTasks.get(taskId); if (task != null && !task.isDone()) { task.cancel(true); } } }
事件类就是简单继承ApplicationEvent的POJO,比如ImportProgressEvent携带taskId和进度百分比就行。前端用SSE的话,后端写个接口返回SseEmitter,订阅这些事件推送给前端。
复杂批处理场景:Spring Batch
如果你的导入任务是企业级的大型批处理(比如百万级数据、多步骤处理、需要重启失败任务),那Spring Batch绝对是首选——它就是专门为这类场景设计的,自带:
- 内置的进度追踪:通过
JobExecution、StepExecution可以实时获取任务完成率 - 原生的任务中断:用
JobOperator就能直接中断正在运行的任务 - 还有任务重启、异常重试、数据校验等企业级功能
- 你可以把任务状态存在Spring Batch的
JobRepository里,前端通过接口查询这些状态来展示进度
对比你当前的WebSocket方案
你现在用STOMP/WebSocket的方案是完全可行的,但确实会增加会话维护的复杂度(比如要存会话指针、处理连接断开的情况)。如果你的应用本来就用WebSocket做其他实时功能(比如聊天、通知),那继续用没问题;但如果只是为了导入任务的进度和中断,上面的方案会更轻量,减少不必要的依赖和维护成本。
最后提醒几个注意点:
- 任务ID一定要用唯一值(比如UUID),避免冲突
- 存任务的缓存要注意内存泄漏,任务完成/失败/中断后一定要从缓存里移除
- 异步线程池的大小要根据服务器配置合理设置,别把服务器资源占满
内容来源于stack exchange
相关产品推荐
相关产品推荐

