如何监控数据处理进度并将状态从Spring Boot后端推送到React UI
实现方案总览
Spring Boot向前端推送进度有3种常用的落地方式,按你的场景(单用户任务、实时进度推送、实现复杂度低优先)排序如下:
方案1:SSE(Server-Sent Events)最适配你的场景
SSE是HTTP协议自带的服务端单向推送能力,前端React不需要额外引入Socket依赖,实现成本极低,非常适合这种单向进度推送的场景。
后端实现步骤
- 首先写一个Controller接口,返回
SseEmitter对象,用于和前端建立长连接
@RestController @RequestMapping("/task") public class TaskController { // 存储任务id和对应emitter的映射,实际生产建议用Guava Cache加过期时间 private final Map<String, SseEmitter> emitterMap = new ConcurrentHashMap<>(); @GetMapping("/progress/{taskId}") public SseEmitter getProgress(@PathVariable String taskId) { // 设置超时时间,建议设得比任务最长执行时间大30s以上 SseEmitter emitter = new SseEmitter(60 * 1000L); emitterMap.put(taskId, emitter); // 连接断开时清除缓存 emitter.onCompletion(() -> emitterMap.remove(taskId)); emitter.onError((e) -> emitterMap.remove(taskId)); emitter.onTimeout(() -> emitterMap.remove(taskId)); return emitter; } }
- 然后在你的转换处理逻辑里,每完成10%就通过对应emitter推送进度:
// 你的业务处理方法 public void processRecords(String taskId, List<Record> records) { int total = records.size(); int processed = 0; int lastReportedProgress = 0; for (Record record : records) { // 你的转换逻辑 convertRecord(record); processed++; int currentProgress = (processed * 100) / total; // 每到下一个10%的节点才推送,避免频繁发消息 if (currentProgress >= lastReportedProgress + 10) { lastReportedProgress = currentProgress; SseEmitter emitter = emitterMap.get(taskId); if (emitter != null) { try { // 推送JSON格式的进度,前端可以直接解析 emitter.send(Map.of("progress", currentProgress, "status", "processing")); // 进度到100%时关闭连接 if (currentProgress >= 100) { emitter.send(Map.of("progress", 100, "status", "completed")); emitter.complete(); } } catch (IOException e) { // 推送失败说明连接已断开,清除缓存 emitterMap.remove(taskId); } } } } }
注意:转换处理逻辑要异步执行,不能和SSE接口的请求同步阻塞,建议用
@Async注解或者线程池提交任务。
前端React实现步骤
- 前端建立SSE连接监听进度即可:
const TaskProgress = ({ taskId }) => { const [progress, setProgress] = useState(0); const [status, setStatus] = useState('pending'); useEffect(() => { const eventSource = new EventSource(`/api/task/progress/${taskId}`); eventSource.onmessage = (event) => { const data = JSON.parse(event.data); setProgress(data.progress); setStatus(data.status); if (data.status === 'completed') { eventSource.close(); } }; eventSource.onerror = () => { eventSource.close(); setStatus('error'); }; return () => eventSource.close(); }, [taskId]); return <div>处理进度:{progress}%</div>; };
方案2:WebSocket 适合需要双向交互的场景
如果你的业务后续还要扩展前端给后端发指令的需求(比如终止任务),可以用WebSocket实现:
- 后端引入
spring-boot-starter-websocket依赖,配置WebSocket处理器,和SSE逻辑类似,每到10%节点往对应session发送进度消息即可。 - 前端用原生
WebSocketAPI或者socket.io-client库监听消息即可。
方案3:前端轮询 适合临时快速实现的小项目
如果不想搞长连接,也可以让前端每隔1~2秒发请求查询进度,后端用缓存存储当前任务的进度值即可,好处是实现最简单,坏处是有一定的进度延迟,服务端压力比长连接略高。
内容的提问来源于stack exchange,提问作者user1354825
相关产品推荐
相关产品推荐

