用 SseEmitter 实现服务器推送:让你的网页像直播一样实时更新

1,807 阅读3分钟

引言

在这个信息爆炸的时代,谁不想让自己的网页像直播一样实时更新呢?想象一下,你在看一个美食博客,突然弹出一条消息:“厨师刚刚把烤箱预热到了200°C!”是不是觉得特别酷?这就是 Server-Sent Events(简称 SSE)的魅力所在。

什么是 Server-Sent Events

Server-Sent Events 就像是你的网页和服务器之间的“小喇叭”,服务器可以通过这个“小喇叭”随时向你的网页发送消息,而你完全不需要不停地刷新页面。它的主要优点包括:

  • 简单:实现起来比你想象的简单多了,不需要复杂的握手过程。
  • 轻量:只需要一个 HTTP 连接,不会让你的电脑喘不过气来。
  • 兼容性好:大多数现代浏览器都支持 SSE,不用担心用户用的是什么奇葩浏览器。

SseEmitter 是什么?

在 Spring Boot 中,SseEmitter 是一个用来实现 SSE 的神器。它就像一个魔法棒,轻轻一挥,你的服务器就能向客户端发送实时数据了。接下来,我们通过一个贴近生活的例子来展示如何使用 SseEmitter

示例:实时厨房更新

假设你是一个美食博主,正在直播制作一道美味的蛋糕。你需要将每个步骤的更新实时推送到前端页面,让观众们知道你正在做什么。我们将使用 SseEmitter 来实现这个功能。

1. 创建控制器

首先,我们在 Spring Boot 项目中创建一个控制器,用于处理 SSE 请求。

package com.timechee.module.infra.controller.admin.qrCode;

import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@RestController
@RequestMapping("/kitchen")
@EnableScheduling
public class KitchenController {

    private final ConcurrentMap<Long, SseEmitter> emitters = new ConcurrentHashMap<>();

    @GetMapping("/stream")
    public SseEmitter streamKitchenUpdates() {
        SseEmitter emitter = new SseEmitter(Long.MAX_VALUE); // 设置超时时间为 Long.MAX_VALUE
        emitters.put(System.currentTimeMillis(), emitter);

        emitter.onCompletion(() -> emitters.remove(emitters.keySet().toArray()[0]));
        emitter.onTimeout(() -> emitters.remove(emitters.keySet().toArray()[0]));

        sendKitchenUpdate(emitter, "The kitchen live broadcast has begun");
        return emitter;
    }

    @Scheduled(fixedRate = 5000) // 每5秒发送一次厨房更新
    public void sendKitchenUpdates() {
        String kitchenUpdate = getLatestKitchenUpdate();
        for (SseEmitter emitter : emitters.values()) {
            sendKitchenUpdate(emitter, kitchenUpdate);
        }
    }

    private void sendKitchenUpdate(SseEmitter emitter, String update) {
        try {
            emitter.send(SseEmitter.event()
                    .id(String.valueOf(System.currentTimeMillis()))
                    .name("kitchen")
                    .data(update));
        } catch (IOException e) {
            emitter.completeWithError(e);
        }
    }

    private String getLatestKitchenUpdate() {
        // 模拟从厨房获取最新更新
        return new Date() + " is stirring the batter, and the aroma is overflowing!";
    }
}

image.png

解释

  1. 控制器
    • streamKitchenUpdates 方法创建一个 SseEmitter 对象,并将其存储在一个 ConcurrentMap 中。这样,我们可以在定时任务中访问这些 SseEmitter 对象。
    • sendKitchenUpdate 方法用于发送厨房更新。它使用 SseEmitter.event() 方法创建一个事件对象,并将其发送给客户端。
    • sendKitchenUpdates 方法是一个定时任务,每5秒调用一次,获取最新的厨房信息并发送给所有连接的客户端。
    • getLatestKitchenUpdate 方法模拟从厨房获取最新更新。

结语

通过 SseEmitter,你可以轻松地实现服务器向客户端的实时数据推送。无论是厨房直播、股票行情还是其他实时数据,SseEmitter 都能帮助你高效地实现这一功能。希望这篇博客能让你对 SseEmitter 有更深入的了解,并在实际项目中应用起来!

如果你觉得这篇文章有趣,不妨分享给你的朋友们,让他们也一起来感受 SseEmitter 的魅力吧!如果有任何问题或建议,欢迎随时留言交流。记得给我点个赞哦,你的支持是我最大的动力!


希望你喜欢这篇博客!如果有任何问题或建议,欢迎随时留言交流。