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

如何监控数据处理进度并将状态从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 23:15:02