springboot+vue实现SSE服务器发送事件

思路

一个基于订阅发布机制的SSE事件。客户端可以请求订阅api(携带客户端id),与服务器建立SSE链接;后续服务器需要推送消息到客户端时,再根据客户端id从已建立链接的会话中找到目标客户端,将消息推送出去。

后端

这个控制器类允许客户端订阅、接收消息和断开连接,通过 pool 存储 SseEmitter 并对其进行管理。

package com.example.q11e.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;@RestController
public class SseController {// 存储已订阅的客户端的会话列表private final Map<String, SseEmitter> pool = new ConcurrentHashMap<>();// 向特定的 SseEmitter 发送消息public void publisher(String id_sid, int content) {// 根据 id_sid 从映射中获取 SseEmitterSseEmitter sseEmitter = pool.get(id_sid);if (Objects.isNull(sseEmitter)) {return;}try {sseEmitter.send(content); // 发送内容} catch (IOException e) {System.out.println("null " + e);}}// 处理客户端的订阅请求@GetMapping("/subscribe/{id}")public SseEmitter subscribe(@PathVariable("id") String id_sid) {// 根据 id_sid 从映射中获取 SseEmitterSseEmitter sseEmitter = pool.get(id_sid);if (Objects.isNull(sseEmitter)) {// 如果不存在,则创建一个新的 SseEmitter,设置超时时间为 130000 毫秒sseEmitter = new SseEmitter(130000L);// 设置发送完成事件:从映射中移除该 SseEmittersseEmitter.onCompletion(() -> pool.remove(id_sid)); // 设置超时事件:从映射中移除该 SseEmittersseEmitter.onTimeout((() -> pool.remove(id_sid))); // 将新创建的 SseEmitter 放入映射中pool.put(id_sid, sseEmitter);}// System.out.println(pool);// 返回 SseEmitter 给客户端return sseEmitter;}// 处理客户端的断开连接请求public void disconnect(String id_sid) {SseEmitter emitter = pool.remove(id_sid);if (emitter!= null) {emitter.complete();}}
}

发送消息

package com.example.q11e.service;import com.example.q11e.controller.SseController;@Service
public class BatchService {@Autowiredpublic BatchService(SseController sseController) {this.sseController = sseController;}private final SseController sseController;@Asyncpublic void batchRequests(){// uid+"_"+sid 客户端标识符,sucCount为需要发送的信息sseController.publisher(uid + "_" + sid, sucCount);sseController.disconnect(uid + "_" + sid);}
}

前端

SSE状态管理  store.ts

// sse前端
import { defineStore } from 'pinia';
import { getUserBalance } from '@/request/api.ts'export const useESStore = defineStore('EventSource', {state: () => ({uid: localStorage.getItem('uid'),balance: 1,eventSourceInstance: null as EventSource | null, // 新增状态属性currentSid: null as string | null,currentCount: 0,currentTotal: 0,connect: false}),actions: {setUid(uid:string) {this.uid = uid;},setConnect(connect: boolean) {this.connect = connect},initEventSource(sid:string) {if (this.uid) {const sseURL = import.meta.env.VITE_SSE_URLconst evtSrcInstance = new EventSource(sseURL + "/" + this.uid + "_" + sid);evtSrcInstance.onmessage = (e) => {this.setCurrentCount(e.data) //普通函数时: this-->evtSrcInstance};evtSrcInstance.onopen = () => {this.setCurrentCount(0)this.setConnect(true)};evtSrcInstance.onerror = () => {this.setConnect(false)this.setCurrentTotal(0)};this.eventSourceInstance = evtSrcInstance; // 存储实例到状态}},closeEventSource() {if (this.eventSourceInstance) {this.eventSourceInstance.close();this.eventSourceInstance = null;}}}
});
<template><span v-show="connect"><span class="sid">{{ sid }}</span><span v-for="(char, index) in ['.', '.', '.']" :key="index" class="blink-effect sid":style="{ animationDelay: `${index * 0.1}s` }">{{ char }}</span><!----count是服务器推送的内容-----><span class="process">{{ count }}/{{ total }}</span></span>
</template><script lang="ts" setup>
import { computed } from 'vue'
import { useESStore } from '@/store/store.ts'
const SSE = useESStore()
let count = computed(() => SSE.currentCount)
let total = computed(() => SSE.currentTotal)
let sid = computed(() => SSE.currentSid)
let connect = computed(() => SSE.connect)
</script><style scoped>
.process {background: red;color: white;padding: 2px 4px;
}
.sid { color: #333; }
@keyframes blink {0%, 100% {transform: translateY(0); /* 开始和结束状态位置无变化 */}50% {transform: translateY(-5px); /* 中间状态位置向上移动5px */}
}
.blink-effect {display: inline-block;animation: blink 1s infinite;
}
</style>

效果

后端执行某耗时任务时,需要实时推送进度到客户端,每完成一个阶段,就向客户端推送一个单位进度,做到客户端实时显示进度的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/500367.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【阻塞队列】- ArrayBlockingQueue 的原理-迭代器

文章目录 1. 前言2. 迭代器3. Itrs3.1 参数3.2 迭代器 Itr3.2.1 参数3.2.2 构造器3.2.3 hasNext3.2.4 next3.2.5 remove3.2.6 shutdown3.2.7 removedAt3.2.8 takeIndexWrapped 3.3 doSomeSweeping&#xff08;tryHandler&#xff09;3.4 register3.5 takeIndexWrapped3.6 remov…

人工智能与传统编程的主要区别是什么?

传统编程&#xff1a;开发者预先编写软件行为规则&#xff0c;代码基于程序员定义逻辑处理输入并产生确定输出&#xff0c;具有确定性、手动编写规则和结构化逻辑特点&#xff0c;如垃圾邮件分类程序基于预设关键词等规则。AI 编程&#xff1a;从数据中学习而非手动编写规则&am…

SpringBoot使用TraceId日志链路追踪

项目场景&#xff1a; ??有时候一个业务调用链场景&#xff0c;很长&#xff0c;调了各种各样的方法&#xff0c;看日志的时候&#xff0c;各个接口的日志穿插&#xff0c;确实让人头大。为了解决这个痛点&#xff0c;就使用了TraceId&#xff0c;根据TraceId关键字进入服务…

ts总结一下

ts基础应用 /*** 泛型工具类型*/ interface IProps {id: string;title: string;children: number[]; } type omita Omit<IProps, id | title>; const omitaA: omita {children: [1] }; type picka Pick<IProps, id | title>; const pickaA: picka {id: ,title…

八大排序——直接插入排序

直接插入排序&#xff08;Straight Insertion Sort&#xff09;&#xff0c;通常简称为插入排序&#xff0c;是一种简单直观的排序算法。它的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。本文…

【QGIS入门实战精品教程】7.3:QGIS制作千层饼(DEM+等高线+影像+TIN)

文章目录 一、效果展示二、数据准备三、制作过程1. 打开软件2. 添加图层3. 制作千层饼一、效果展示 二、数据准备 订阅专栏后,从专栏配套案例数据包中的7.3.rar中获取。 1. dem 2. 影像 3. 等高线 4. tin 三、制作过程 1. 打开软件 打开QGIS软件。 QGIS软件主界面。

NetSuite Formula(HTML)超链打开Transaction

当Saved Search作为Sublist应用在Form时&#xff0c;如果Document Number是Group过的&#xff0c;则会出现如下超链失效的情况。 解决办法&#xff1a; 可以利用Saved Search中的Formula&#xff08;HTML&#xff09;功能来构建超链&#xff0c;用于打开Transaction。 以下图…

Springboot3.x整合swagger3

在网上看了许多教程&#xff0c;发现很多都是针对Spring Boot 2 框架的&#xff0c;即使有针对Spring Boot 3 的&#xff0c;用法也不太一样,配置项经常找不到类&#xff0c;经过对比测试&#xff0c;最后我使用的是 SpringDoc OpenAPI Starter WebMvc UI. pom为 <!--swag…

android.enableJetifier=true的作用:V4包的类自动编程成了androidx包的类,实现androidx的向下兼容

结论&#xff1a;引入androidx包后&#xff0c;可以兼容旧版本v4包的插件&#xff0c;把之前的 implementation com.yinglan.alphatabs:library:1.0.8 引入的组件中使用v4包的类&#xff0c;里面V4包自动反编译成 androidx包的类 结论; ‌V4包的类自动编程成了androidx包的…

详解MySQL在Windows上的安装

目录 查看电脑上是否安装了MySQL 下载安装MySQL 打开MySQL官网&#xff0c;找到DOWNLOADS 然后往下翻&#xff0c;找到MySQL Community(GPL) Downloads>> 然后找到MySQL Community Server 然后下载&#xff0c;选择No thanks,just start my download. 然后双击进行…

excel操作

来源&#xff1a;B站默默亚 一、版本识别 特点&#xff1a;向后兼容&#xff1b;高版本可以打开低版本&#xff0c;低版本不可以打开高版本 工作中&#xff0c;给老板最低版本&#xff0c;即2003版本 二、文件的扩展名 三、excel页面

最大化堡垒补给数量的策略与实现

最大化堡垒补给数量的策略与实现 问题描述输入格式输出格式问题分析解决方案代码实现代码解释问题描述 可怕的战争发生了,小度作为后勤保障工作人员,为了保卫国家而努力。现在有 N 个堡垒需要补给,然而总的预算 B 是有限的。每个堡垒需要价值 P(i) 的补给,并且需要 S(i) 的…

手机实时提取SIM卡打电话的信令声音-双卡手机来电如何获取哪一个卡的来电

手机实时提取SIM卡打电话的信令声音 --双卡手机来电如何获取哪一个卡的来电 一、前言 前面的篇章《手机实时提取SIM卡打电话的信令声音-智能拨号器的双SIM卡切换方案》中&#xff0c;我们论述了局域网SIP坐席通过手机外呼出去时&#xff0c;手机中主副卡的呼叫调度策略。 但…

国产手机嘴上喊着挑战苹果,实际行动却已承认失败,真的干不过

国产手机年年喊着挑战苹果&#xff0c;在苹果的iPhone15和iPhone16都被诟病创新不足的时候&#xff0c;国产手机更是以为迎来了赶超苹果的机会&#xff0c;然而随着年底的到来&#xff0c;诸多国产手机品牌的实际行动却说明他们其实已经承认败给苹果了。 近几周&#xff0c;国产…

微信小程序:定义页面标题,动态设置页面标题,json

1、常规设置页面标题 正常微信小程序中&#xff0c;设置页面标题再json页面中进行设置&#xff0c;例如 {"usingComponents": {},"navigationBarTitleText": "标题","navigationBarBackgroundColor": "#78b7f7","navi…

鸿蒙应用开发启航计划

以前有过简单的学习了解&#xff0c;但是现在工作内容的原因&#xff0c;要专门搞这个&#xff0c;因此需要更加熟练地掌握鸿蒙应用开发。 1.开发IDE -- DevEco Studio Windows环境 运行环境要求 为保证DevEco Studio正常运行&#xff0c;建议电脑配置满足如下要求&#xff…

LabVIEW 实现自动对焦的开发

自动对焦&#xff08;Autofocus, AF&#xff09;技术是通过分析图像或传感器信号&#xff0c;动态调整焦点位置以实现清晰成像或高精度定位的过程。在LabVIEW中&#xff0c;可以通过集成信号采集、数据处理、控制算法和硬件接口模块&#xff0c;实现多种自动对焦方法&#xff0…

网络安全 | 物联网安全:从设备到网络的全方位防护

网络安全 | 物联网安全&#xff1a;从设备到网络的全方位防护 一、前言二、物联网设备安全2.1 物联网设备的特点与安全风险2.2 物联网设备安全防护策略 三、物联网网络通信安全3.1 物联网网络通信的安全挑战3.2 物联网网络通信安全防护措施 四、物联网数据安全4.1 物联网数据的…

C# OpenCV机器视觉:目标跟踪

在一个阳光明媚的下午&#xff0c;阿强正在实验室里忙碌&#xff0c;突然他的同事小杨走了进来&#xff0c;脸上挂着一丝困惑。 “阿强&#xff0c;我的目标跟踪项目出了问题&#xff01;我想跟踪一个移动的物体&#xff0c;但总是跟丢&#xff01;”小杨一边说&#xff0c;一…

JSON结构快捷转XML结构API集成指南

JSON结构快捷转XML结构API集成指南 引言 在当今的软件开发世界中&#xff0c;数据交换格式的选择对于系统的互操作性和效率至关重要。JSON&#xff08;JavaScript Object Notation&#xff09;和XML&#xff08;eXtensible Markup Language&#xff09;是两种广泛使用的数据表…