基于 Spring Boot + SSE 实现和风天气每分钟实时推送(完整教程)
本文将带你从 普通请求式接口 改造为 Server-Sent Events (SSE) 长连接推送,并实现 每分钟主动推送和风天气实时天气 数据,实现大屏、监控平台、移动端的实时天气刷新。
1. 背景需求
原本系统的天气接口如下:
@GetMapping("/now")
public JSONObject now(@RequestParam String location) {
return client.getWeatherNow(location);
}
这是一个典型的 短连接 + 请求式 API。每次前端想要最新天气,都必须主动发起 HTTP 请求。
缺点:
-
⛔ 无法实时更新
-
⛔ 大屏需要不断轮询,浪费资源
-
⛔ 并发多时服务器压力大
为了解决这些问题,我们改造为:
⭐ 基于 SSE(Server-Sent Events)的天气实时推送服务
-
前端只需发起一次连接
-
服务端每 60 秒主动推送一次天气
-
支持自动重连
-
适合大屏场景、监控终端、物联网设备
2. SSE 原理快速说明
SSE(Server-Sent Events)是 HTML5 原生支持的 服务器单向推送 技术。
特点:
-
服务端 → 客户端 单向消息
-
前端自动重连(EventSource 内置)
-
基于 HTTP,兼容性好
-
天生适合大屏推送
与 WebSocket 对比:
| 能力 | SSE | WebSocket |
|---|---|---|
| 单向推送 | ✔ | ✔ |
| 双向通信 | ✘ | ✔ |
| 心跳机制 | 内置 | 需自定义 |
| 前端易开发 | 非常简单 | 略复杂 |
| 适用场景 | 大屏、监控推送 | 游戏、聊天 |
因此天气推送用 SSE 是最佳选择。
3. 最终效果演示
前端只需:
const evt = new EventSource("/weather/sse/now?location=101280601");
evt.onmessage = (e) => {
console.log("实时天气:", JSON.parse(e.data));
};
服务端每分钟自动返回:
{
"temp": "25",
"text": "晴",
"updateTime": "2025-11-21 16:30:02"
}
4. Spring Boot SSE 服务端改造完整代码
4.1 推送 Controller
@RestController
@RequestMapping("/weather/sse")
public class WeatherSseController {
private final QWeatherClient client;
private final Map<String, SseEmitter> emitterMap = new ConcurrentHashMap<>();
public WeatherSseController(QWeatherClient client) {
this.client = client;
}
@GetMapping("/now")
public SseEmitter now(@RequestParam String location) {
SseEmitter emitter = new SseEmitter(0L);
// 保存上一次成功的天气数据
final JSONObject[] lastData = new JSONObject[1];
executor.scheduleAtFixedRate(() -> {
try {
JSONObject data = weatherService.getWeatherSimple(location);
if (data != null) {
lastData[0] = data; // 保存最新成功数据
} else if (lastData[0] != null) {
// API 返回空,保持上次数据
data = lastData[0];
} else {
// 第一次就失败,返回默认提示
JSONObject fallback = new JSONObject();
fallback.put("temp", "--");
fallback.put("text", "天气数据不可用");
fallback.put("windDir", "--");
fallback.put("windScale", "--");
fallback.put("updateTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
data = fallback;
}
emitter.send(SseEmitter.event()
.name("weather")
.data(data.toJSONString()));
} catch (IOException e) {
// SSE 连接被关闭或异常
log.error("SSE 推送异常: " + e.getMessage());
} catch (Exception e) {
log.error("获取天气数据异常: " + e.getMessage());
}
}, 0, 1, TimeUnit.MINUTES);
emitter.onCompletion(() -> log.error("SSE 已完成"));
emitter.onTimeout(() -> log.error("SSE 超时"));
emitter.onError((e) -> log.error("SSE 连接出错: " + e.getMessage()));
return emitter;
}
}
⭐ 关键点说明:
-
SseEmitter(0L):永不超时 -
使用
ScheduledExecutorService每分钟推送一次 -
自动关闭连接,避免资源泄漏
-
name("weather")前端可按事件名监听
5. QWeather 和风天气 Client 实现
@Component
public class QWeatherClient {
@Value("${qweather.key}")
private String apiKey;
@Value("${qweather.host}")
private String apiHost;
private final RestTemplate restTemplate = new RestTemplate();
public JSONObject getWeatherNow(String location) {
String url = String.format(
"%s/v7/weather/now?location=%s&key=%s",
apiHost, location, apiKey
);
String resp = restTemplate.getForObject(url, String.class);
return JSON.parseObject(resp);
}
}
6. application.yml 配置
qweather:
key: 你的key
host: https://api.qweather.com
7. 前端监听 SSE(可直接用在大屏)
const source = new EventSource(`/weather/sse/now?location=101280601`);
// 接收消息
source.addEventListener("weather", function (event) {
const data = JSON.parse(event.data);
console.log("最新天气", data);
});
// 自动重连
source.onerror = () => {
console.log("连接断开,客户端会自动重连...");
};
前端不需要手写重连逻辑,EventSource 会在网络断开后自动 reconnect。
8. 项目实战中的典型应用场景
✔ 大屏城市天气监控
✔ 企业气象监控系统
✔ 气象数据驱动的自动化业务(如下雨时自动关闭户外设备)
✔ IoT 设备屏幕周期天气更新
✔ 小程序与 Web 可共用接口
9. 对比轮询的性能优势
| 指标 | 轮询 | SSE |
|---|---|---|
| 请求次数 | 每分钟 N 次用户 × | 仅 1 次长期连接 |
| 网络消耗 | 高 | 极低 |
| 延迟 | 依赖轮询间隔 | 实时 |
| 并发表现 | 差 | 优秀 |
SSE 特别适合天气这种稳定低频数据推送。
10. 总结
本文完整实现:
-
基于 SSE 的天气实时推送服务端
-
和风天气 API 的实时查询
-
任务调度每分钟推送
-
前端 EventSource 实时更新
更多推荐


所有评论(0)