Spring MVC中从Rest Controller调用Scheduler并获取定时任务返回结果
解决方案
1 改造定时任务类,实现结果共享
首先需要在线程安全的前提下存储定时任务每次执行的最新结果,直接修改你现有的StartupBean即可,Spring单例模式下Controller和定时任务可以共享该Bean内的成员变量:
@Component public class StartupBean { private static final Logger LOGGER = LoggerFactory.getLogger(StartupBean.class); // volatile保证多线程下的结果可见性,存储定时任务返回的JSON对象 private volatile Object latestSessionCountResult; // 标记任务运行状态,避免重复启动Timer private volatile boolean isRunning = false; public void start() throws Exception { if (isRunning) { return; } isRunning = true; LOGGER.info("Entering start method {}", System.currentTimeMillis()); try { TimerTask counterTimerTask = new SessionCountTask() { @Override public void run() { // 此处替换为你SessionCountTask中实际返回JSON的方法名 Object jsonResult = this.generateJsonResult(); // 更新最新结果 latestSessionCountResult = jsonResult; } }; Timer counterTimer = new Timer(true); counterTimer.scheduleAtFixedRate(counterTimerTask, 0, 10 * 1000L); } catch (Exception e) { isRunning = false; LOGGER.error("Error in start method of startup Bean {}", e); } finally { LOGGER.info("Exiting start method {}", System.currentTimeMillis()); } } // 对外提供获取最新结果的方法 public Object getLatestSessionCount() { return latestSessionCountResult; } }
2 Controller层实现
根据你的业务场景二选一即可:
场景1:前端主动轮询获取结果(实现最简单)
前端每10秒调用一次接口拉取最新数据即可:
@RestController @RequestMapping("/session") public class SessionCountController { @Autowired private StartupBean startupBean; // 手动触发定时任务的接口,不需要可以删掉 @PostMapping("/startTask") public void startCountTask() throws Exception { startupBean.start(); } // 获取最新统计结果 @GetMapping("/count") public Object getLatestCount() { return startupBean.getLatestSessionCount(); } }
场景2:服务端主动推送结果(无需前端轮询)
用Spring原生支持的SSE实现服务端主动推送,前端建立一次连接就能持续收到最新结果:
@GetMapping(value = "/count/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter streamCountResult() { SseEmitter emitter = new SseEmitter(); new Thread(() -> { try { while (true) { Object result = startupBean.getLatestSessionCount(); if (result != null) { emitter.send(result); } Thread.sleep(10000); } } catch (Exception e) { emitter.completeWithError(e); } }).start(); return emitter; }
3 优化建议
- 不推荐使用JDK原生
Timer实现定时任务,单线程运行的特性会导致任务抛出异常后整个调度直接终止,建议替换为Spring自带的@Scheduled注解,开启@EnableScheduling后直接在方法上加@Scheduled(fixedRate = 10000)即可,无需手动管理调度线程生命周期。 - 如果有存储历史结果、多节点共享结果的需求,可以把执行结果写入Redis或本地缓存,扩展能力更强。
内容的提问来源于stack exchange,提问作者vr3w3c9
相关产品推荐
相关产品推荐

