SSE 实践:用 Vue 和 Spring Boot 实现实时数据传输
前言
大家好,我是雪荷。最近我在灵犀 BI 项目中引入了 SSE 技术,以保证图表的实时渲染,当图表渲染完毕服务端推送消息至浏览器端触发重新渲染。
什么是 SSE?
SSE 全称为 Server-Send Events 意思是服务端推送事件。

SSE 相比于 Websocket 它基于 Http 实现无需实现其他协议、更加轻量级、支持自动重连,但只能服务端推送消息给客户端,客户端不能推送消息给服务端。适用于服务端主动推送数据,客户端只需接收数据的场景。以下为 SSE 与 Websocket 的对比:
| SSE | Websocket | |
|---|---|---|
| 通信 | 单向通信(服务端到客户端) | 双向通信 |
| 协议 | Http | Websocket |
| 自动重连 | 支持 | 不支持,要客户端自行支持 |
| 数据格式 | 文本格式,如果要二进制数据得自行编码 | 默认二进制数据、支持文本格式 |
| 浏览器兼容性 | 大部分支持 | 主流浏览器支持较好 |
Vue + Spring Boot 实现一个 SSE demo
后端实现
由于 SSE 基于 http 协议实现,因此我们无需引入第三方 jar 包。首先通过 idea 创建一个 spring boot 项目,记得勾选 spring web 依赖。

创建一个跨域配置,因为前后端的端口不同,会出现请求跨域。
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOriginPatterns("*").allowedMethods("GET", "POST", "PUT", "DELETE").allowedHeaders("*").allowCredentials(true);}
}
随后创建一个 SSEcontroller,用来创建 SSE 连接和推送消息给客户端。
@RestController
@RequestMapping("/sse")
public class SSEController {
private static final Map<Integer, SseEmitter> sseEmitters = new ConcurrentHashMap<>();
@GetMapping("/create")public SseEmitter createSSEConnection() {// 创建SSE连接SseEmitter emitter = new SseEmitter();// 将 sseEmitter 赛道 map 中,方便管理多个 sse 连接,比如可以为每个用户创建一个 sse 连接,以用户 id 为 map 的 keysseEmitters.put(1, emitter);emitter.onCompletion(() -> sseEmitters.remove(1));emitter.onTimeout(() -> sseEmitters.remove(1));return emitter;}
@PostMapping("/send")public void sendSSEMessage() {// 发送消息SseEmitter sseEmitter = sseEmitters.get(1);if (sseEmitter != null) {for (int i = 0; i < 3; i++) {try {sseEmitter.send(SseEmitter.event().data("hello world") // 发送的数据,是 Object 类型.name("sse-message")); // 定义 SSE 事件的名称,方便前端监听} catch (IOException e) {sseEmitters.remove(1);}}}}
}
在上面的代码中,createSSEConnection 用来创建 sse 连接,注意其请求方法为 get,post 是不能创建的,因为前端发的就是 get 请求。我还创建了一个 sseEmitters 的 Map<Integer, SseEmitter> 来管理 sse 连接,在项目中 map 的 key 可以作为客户端标识,可以是用户 id 或者某些业务参数。当创建完毕后将 sseEmiter 塞到 map 中,当 sse 连接超时或者完成就删掉在 map 中对应的 value。
而 sendSSEMessage 方法就是服务端向客户端推送消息的实例代码,这里我们方便我写了一个请求来触发服务端发消息给客户端,实际是根据具体业务来主动推消息给客户端。比如异步生成图表或者插入数据时,当此动作完毕后可以发消息给前端表明操作成功。那如何发送消息就不用我说了,先从 map 取出 sseEmitter,接着添加事件名和要发送的数据就好了。
如果是前端传送参数来创建 sse 连接的话,可以这样操作。
@GetMapping("/create")public SseEmitter createSSEConnection(@RequestParam Integer id) {// 创建SSE连接SseEmitter emitter = new SseEmitter();sseEmitters.put(id, emitter);emitter.onCompletion(() -> sseEmitters.remove(1));emitter.onTimeout(() -> sseEmitters.remove(1));return emitter;}
前端实现
首先创建一个 vue 项目,引入一些基础依赖,比如 aixos,项目具体结构如下:

myAxios.js:
import axios from 'axios';
const myAxios = axios.create({baseURL: 'http://localhost:8080',
})
myAxios.defaults.withCredentials = true; //设置为true
export default myAxios;
然后 SSEDemo.vue 就是主要创建 sse 连接和接受服务端发送消息
SSEDemo.vue:
<template><div><h3>接收到的消息:</h3><ul><li v-for="(message, index) in messages" :key="index">{{ message }}</li></ul></div><button @click="sendMessage">发送消息</button>
</template>
<script>
import {onBeforeUnmount, onMounted, ref} from 'vue';
import myAxios from "@/plugins/myAxios.js";
export default {setup() {const messages = ref([]); // 存储接收到的消息let eventSource = null; // 用于保存 EventSource 实例
onMounted(() => {// 创建 EventSource 实例,连接到服务器端的 SSE 端点eventSource = new EventSource('http://localhost:8080/sse/create');
// 监听特定事件名 'sse-message'eventSource.addEventListener('sse-message', (event) => {console.log('收到消息:', event.data);// 解析消息并保存到 messages 中messages.value.push(event.data);});
// 监听连接关闭或错误eventSource.onerror = (error) => {console.error('SSE 连接出错:', error);eventSource.close(); // 关闭连接};});
onBeforeUnmount(() => {// 组件销毁时关闭 SSE 连接if (eventSource) {eventSource.close();}});
const sendMessage = async () => {await myAxios.post('/sse/send');}
return {messages,sendMessage};},
};
</script>
new EventSource 新建一个事件源,其会向后端发送请求方法为 get 的 http 请求,利用 eventSource.addEventListener 创建一个事件监听器并带上事件名就可以完成事件的监听和消息发送啦。
最后运行前后端项目,打开前端地址会看到有一个 http 显示待处理。

点击发送消息按钮使后端发送消息给前端,前端接收到就会实时显示。

开源项目
厚米匹配
网址:厚米匹配系统
前端仓库:https://github.com/dnwwdwd/homieMatching-fronted
后端仓库:https://github.com/dnwwdwd/homieMatching
灵犀 BI
网址:鱼智能 BI
前端仓库:https://github.com/dnwwdwd/Lingxi-BI-fronted
后端仓库:https://github.com/dnwwdwd/Lingxi-BI
相关文章:
SSE 实践:用 Vue 和 Spring Boot 实现实时数据传输
前言 大家好,我是雪荷。最近我在灵犀 BI 项目中引入了 SSE 技术,以保证图表的实时渲染,当图表渲染完毕服务端推送消息至浏览器端触发重新渲染。 什么是 SSE? SSE 全称为 Server-Send Events 意思是服务端推送事件。 SSE 相比于 …...
TouchGFX学习笔记(一)
配置请参考链接:TouchGFX超低配置移植教程-CSDN博客 一,显示配置 1.适当增加堆栈大小 2.适当增大缓冲大小 双重缓冲消除了任何撕裂的风险,无论渲染下一帧需要多长时间,因为TfT控制器,例如,总是可以访问最…...
Java算法 二叉树入门 力扣简单题相同的树 翻转二叉树 判断对称二叉树 递归求二叉树的层数
目录 模版 先序遍历 中序遍历 后序遍历 力扣原题 相同的二叉树 力扣原题 翻转二叉树 遍历树的层数 题目 静态变量 核心逻辑 模版 // 二叉树public static class Node{public int value;public Node left;public Node right;public Node(int v) {valuev;}} 先序遍历 …...
如何将 session 共享存储到 redis 中
文章目录 一. 分布式 session 登录1.1 什么是分布式?1.2 Session 共享1.3 为什么服务器 A 登录后,请求发到服务器 B,不认识该用户?1.4 共享存储 二. Session 共享实现Redis三. 测试session共享四. cookie设置4.1 前端4.2 后端 一.…...
vue3学习三
五 计算属性 定义 选项式 export default {data(){return {num:1}},computed:{num1(){this.num1}} } 组合式 import {ref,computed} from vuelet numref(0); //仅读 let num1 computed(()>{return num.value1 }) 计算时依赖的变量数据发生变化,则计算属性…...
彻底理解JVM类加载机制
文章目录 一、类加载器和双亲委派机制1.1、类加载器1.2、双亲委派机制1.3、自定义类加载器1.4、打破双亲委派机制 二、类的加载 图片来源:图灵学院 由上图可知,创建对象,执行其中的方法,在java层面,最重要的有获取…...
【计算机体系结构、微架构性能分析】core 与 uncore 分别是哪一些部分?区分 core 和 uncore
在计算机体系结构中,Core 和 Uncore 是描述处理器内部架构的两个重要概念,尤其在多核处理器中更为常见。 1. Core(核心) Core 指的是处理器中的计算核心,是执行指令和处理数据的基本单元。每个核心都包含独立的执行单…...
鸿蒙打包发布
HarmonyOS应用/元服务发布(打包发布) https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/ide-publish-app-V13?catalogVersionV13 密钥:包含非对称加密中使用的公钥和私钥,存储在密钥库文件中,格式…...
vue2:实现上下两栏布局,可拖拽改变高度
要拖拽改变两栏高度,那么总高度要确定,在拖拽的过程中,实时根据光标位置计算两栏高度,所以: 1、最外层有一个box, 高度是屏幕高度screenHeight; 2、该值在页面挂载时获取初始值(window.innerHeight-100),这里减少100,因为窗口上面有工具栏; 3、监听窗口resize事件…...
MongoDB 学习指南:深入探索非关系型数据库
MongoDB学习资料 MongoDB学习资料 MongoDB学习资料 在当今数字化时代,数据量呈爆炸式增长,数据结构也变得愈发复杂多样。传统的关系型数据库在处理一些大规模、高并发以及非结构化数据时,逐渐显露出局限性。而 MongoDB 作为一款领先的非关系…...
天机学堂3-ES+Caffeine
文章目录 day05-问答系统表 用户端分页查询问题目标效果代码实现 3.6.管理端分页查询问题ES相关 管理端互动问题分页实现三级分类3.6.5.2.多级缓存3.6.5.3.CaffeineTODO:使用Caffeine作为本地缓存,另外使用redis或者memcache作为分布式缓存,构…...
FPGA车牌识别
基于FPGA的车牌识别主要包含以下几个步骤:图像采集、颜色空间转换、边缘检测、形态学处理(腐蚀和膨胀)、特征值提取、模板匹配、结果显示。先用matlab对原理进行仿真,后用vivado和modelsim进行设计和仿真。 一、1.图像采集采用ov…...
Pandas库的常用内容归纳
Pandas 是一个强大的 Python 数据分析库,提供了大量用于数据处理和分析的功能。以下是一些 Pandas 库中常用的功能: 数据创建和操作 Series 和 DataFrame:创建一维的 Series 和二维的 DataFrame 对象。数据导入:从 CSV、Excel、…...
R语言的并发编程
R语言的并发编程 引言 在现代计算中,如何有效地利用计算资源进行数据处理和分析已成为一个重要的研究方向。尤其在大数据时代,数据量的急剧增加让单线程处理方式显得力不从心。为了解决这一问题,各种编程语言都开展了并发编程的研究和应用。…...
STM32 FreeRTOS中断管理
目录 FreeRTOS的中断管理 1、STM32中断优先级管理 2、FreeRTOS任务优先级管理 3、寄存器和内存映射寄存器 4、BASEPRI寄存器 5、FreeRTOS与STM32中断管理结合使用 vPortRaiseBASEPRI vPortSetBASEPRI 6、FromISR后缀 7、在中断服务函数中调用FreeRTOS的API函数需注意 F…...
数据结构-栈和队列
文章目录 一、栈1.概念与结构2.数组栈的实现2.1 栈的结构定义2.2 栈的初始化2.3 栈的销毁2.4 栈的判空2.5 栈的入栈2.6 栈的出栈2.7 查看栈顶元素2.8 栈的大小 3.两种栈的图示结构 二、队列1.概念与结构2.链式队列的实现2.1 队列的结构定义2.2 队列的初始化2.3 队列的销毁2.4 队…...
RabbitMQ---TTL与死信
(一)TTL 1.TTL概念 TTL又叫过期时间 RabbitMQ可以对队列和消息设置TTL,当消息到达过期时间还没有被消费时就会自动删除 注:这里我们说的对队列设置TTL,是对队列上的消息设置TTL并不是对队列本身,不是说队列过期时间…...
第4章 Kafka核心API——Kafka客户端操作
Kafka客户端操作 一. 客户端操作1. AdminClient API 一. 客户端操作 1. AdminClient API...
Python爬虫学习前传 —— Python从安装到学会一站式服务
早上好啊,大佬们。我们的python基础内容的这一篇终于写好了,啪唧啪唧啪唧…… 说实话,这一篇确实写了很久,一方面是在忙其他几个专栏的内容,再加上生活学业上的事儿,确实精力有限,另一方面&…...
Lora理解QLoRA
Parameter-Efficient Fine-Tuning (PEFT) :节约开销的做法,fine-tune少量参数,而不是整个模型; Low-Rank Adaptation (LoRA) :是PEFT的一种;冻结原参数矩阵,只更新2个小参数矩阵。 原文经过对比…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
