springboot+js实现SSE消息推送

一、后端
1、新建工具类SseServiceTool

package com.example.system_manage.utils;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** SSE消息推送工具*/
@Slf4j
@Component
public class SseServiceTool {private final static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();//创建一个SSE对象public SseEmitter createSse(String messageId) {closeSseEmitterById(messageId);log.info("创建对象");log.info(sseEmitterMap.size()+"");SseEmitter sseEmitter = new SseEmitter(5_60_000L);// 设置前端的重试时间为1ssseEmitter.onCompletion(()->{log.info("完成传输");});sseEmitter.onError((throwable)->{log.info("出现错误");});sseEmitterMap.put(messageId, sseEmitter);return sseEmitter;}public SseEmitter getSseEmitterById(String id){if(!sseEmitterMap.containsKey(id)){throw new BusinessException("当前无连接对象");}return sseEmitterMap.get(id);}//根据id关闭连接public void closeSseEmitterById(String messageId){if(sseEmitterMap.containsKey(messageId)){sseEmitterMap.get(messageId).complete();sseEmitterMap.remove(messageId);}}
}

2、新建service接口

package com.example.system_manage.service;import java.util.Map;public interface ISseMessageService {//发送消息String pushMessage(Map<String,String> map);//关闭SSE服务String closeSseService(String messageId);
}

3、新建service实现类

package com.example.system_manage.service.impl;import com.example.system_manage.service.ISseMessageService;
import com.example.system_manage.utils.SseServiceTool;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import java.util.Map;@Service
@Slf4j
public class SseMessageServiceImpl implements ISseMessageService {@Resourceprivate SseServiceTool sseServiceTool;@Overridepublic String pushMessage(Map<String, String> map) {try {SseEmitter sseEmitter = sseServiceTool.getSseEmitterById(map.get("id"));Map<String,String> sendMessage=Map.of("title",map.get("title"),"content",map.get("content"));SseEmitter.SseEventBuilder data = SseEmitter.event().name(map.get("eventName")).id(map.get("id")).data(sendMessage);sseEmitter.send(data);}catch (Exception ex){//移除连接sseServiceTool.closeSseEmitterById(map.get("id"));log.error(ex.getMessage());return "noSSE";}return "OK";}@Overridepublic String closeSseService(String messageId) {sseServiceTool.closeSseEmitterById(messageId);return "OK";}
}

4、新建控制器

package com.example.system_manage.controller;import com.example.system_manage.service.ISseMessageService;
import com.example.system_manage.utils.ResultMap;
import com.example.system_manage.utils.SseServiceTool;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import java.util.List;
import java.util.Map;@RestController
@RequestMapping(path = "/sseMessage")
@Slf4j
public class SseMessageController {@Resourceprivate ISseMessageService sseMessageService;@Resourceprivate SseServiceTool sseServiceTool;//创建SSE连接对象@GetMapping(path = "/createSse", produces = {MediaType.TEXT_EVENT_STREAM_VALUE})public SseEmitter createSse(@RequestParam("messageId") String messageId) {return sseServiceTool.createSse(messageId);}//推送消息@PostMapping(path = "/pushMessage")public ResultMap pushMessage(@RequestBody Map<String,String> map) {return ResultMap.SUCCESS.setNewData(sseMessageService.pushMessage(map));}//关闭SSE连接@GetMapping("/closeSseService")public ResultMap closeSseService(@RequestParam("messageId") String messageId){return ResultMap.SUCCESS.setNewData(sseMessageService.closeSseService(messageId));}}

二、前端部分
1、安装依赖

 npm install event-source-polyfill  -S

2、封装requestEventSource.js用于创建SSE对象

import { EventSourcePolyfill } from 'event-source-polyfill';
import cookie from 'js-cookie';/*** 创建一个sse对象* @param {*} data* @returns*/
export const getEventSource = (data) => {return new EventSourcePolyfill(`${data.httpRequest}${data.url}`, {headers: {'xxx': cookie.get("xxxxx"),},});
};

用于获取请求SSE对象

export function getSseMessageObj(data) {return getEventSource({httpRequest: baseUrl,url: `/sseMessage/createSse?messageId=${data}`,});
}

3、页面组件

<!--* @Author: zhangming zhangming@sinoma-tianjin.cn* @Date: 2024-07-15 15:48:08* @LastEditors: zhangming zhangming@sinoma-tianjin.cn* @LastEditTime: 2024-07-16 15:35:23* @FilePath: \zjcgg_system_manage\src\views\demos\OrganizationSelectTest.vue* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>SSE测试<el-button type="primary" @click="pushSseMessageTopLeft">左上角推送消息</el-button><el-button type="primary" @click="pushSseMessageTopRight">右上角推送消息</el-button>
</template><script setup>
import { ref, onMounted, onBeforeMount, onBeforeUnmount, nextTick, reactive, watch } from 'vue';
import { EventSourcePolyfill } from 'event-source-polyfill';
import { ElMessage, ElNotification } from 'element-plus';
import { generateUUID } from '@/utils/index';
import { closeSseService, pushSseMessage, getSseMessageObj } from '@/api/sseClient';const source = ref(null); //sse连接对象
const messageKey = ref('');window.addEventListener('beforeunload', async (event) => {//浏览器窗口事件// 设置returnValue属性可以显示一个提示信息,询问用户是否真的要离开页面await closeSsse();
});window.addEventListener('unload', async (event) => {//浏览器窗口事件// 设置returnValue属性可以显示一个提示信息,询问用户是否真的要离开页面await closeSsse();
});onBeforeMount(async () => {//初始加载messageKey.value = generateUUID();source.value = getSseMessageObj(messageKey.value);source.value.addEventListener('message-top-left',(event) => {let response = JSON.parse(event.data);ElNotification({title: response.title,message: response.content,position: 'top-left',});},false);source.value.addEventListener('message-top-right',(event) => {let response = JSON.parse(event.data);ElNotification({title: response.title,message: response.content,position: 'top-right',});},false);
});onBeforeUnmount(async () => {await closeSsse();
});const closeSsse = async () => {//获取到选择的人source.value.close();await closeSseService(messageKey.value);
};const pushSseMessageTopRight = async () => {let postData = {id: messageKey.value,content: '报警通知',title: '消息主题',eventName: 'message-top-right',};await pushSseMessage(postData);
};const pushSseMessageTopLeft = async () => {let postData = {id: messageKey.value,content: '报警通知',title: '消息主题',eventName: 'message-top-left',};await pushSseMessage(postData);
};
</script><style lang="scss" scoped></style>

注:里面有部分接口是请求后端断开连接用的

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

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

相关文章

【Linux杂货铺】期末总结篇4:shell编程

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️什么是Shell脚本&#xff1f;二. ⛳️Shell 入门三. ⛳️Shell 变量3.1 &#x1f514;变…

Kotlin中Unit、Any和Nothing

Unit Unit是一个特殊的类型&#xff0c;它表示“没有意义的值”的单元类型。在Kotlin中&#xff0c;当你不需要函数返回任何具体值时&#xff0c;可以使用Unit类型。 和Java 中 void一样。 Any 所有非空类的父类 Any?所有类的父类 类似Java中Object Nothing 表示一个函数或…

# 如何解决 App Store 审核中的 4.3(a) 问题:Guideline 4.3(a) - Design - Spam

如何解决 App Store 审核中的 4.3(a) 问题&#xff1a;Guideline 4.3(a) - Design - Spam 4.3(a) 审核问题是指&#xff1a;你的应用与其他开发者提交的应用在二进制文件、元数据和/或概念上存在相似之处&#xff0c;仅有微小差别。这通常会导致你的应用被视为垃圾应用而被拒绝…

java启动springboot项目前根据环境变量动态改编yaml文件的变量值

需求&#xff1a;在学习FC时&#xff0c;启动一个springboot项目时需要由用户填写自己的某些特殊变量&#xff0c;解决方案是在FC中由用户自己添加环境变量&#xff0c;通过java代码获取到环境中的环境变量&#xff0c;在springboot启动前注入到yaml文件中。 1.java获取环境变…

volatile相关知识

volatile的两大作用 保持线程的可见性 可见性&#xff1a;当一个线程修改一个共享变量时&#xff0c;其他的线程可以都可以直到到这个值被修改了。 在每次访问 volatile 变量时&#xff0c;都会直接从主内存中读取最新值&#xff0c;而不会使用线程本地的缓存&#xff0c;从…

旗晟巡检机器人的应用场景有哪些?

巡检机器人作为现代科技的杰出成果&#xff0c;已广泛应用于各个关键场景。从危险的工业现场到至关重要的基础设施&#xff0c;它们的身影无处不在。它们以精准、高效、不知疲倦的特性&#xff0c;担当起保障生产、守护安全的重任&#xff0c;为行业发展注入新的活力。那么&…

如何使用简鹿水印助手或 Photoshop 给照片添加文字

在社交媒体中&#xff0c;为照片添加个性化的文字已经成为了一种流行趋势。无论是添加注释、引用名言还是表达情感&#xff0c;文字都能够为图片增添额外的意义和风格。本篇文章将使用“简鹿水印助手”和“Adobe Photoshop”这两种工具给照片添加文字的详细步骤。 使用简鹿水印…

SpringBoot 解决 getSession().getAttribute() 在负载均衡环境下无法获取session的问题

在Spring Boot中&#xff0c;使用getSession().getAttribute()方法时遇到在负载均衡环境下无法正确获取session属性的问题&#xff0c;通常是由于session属性存储在单个服务器的内存中&#xff0c;而负载均衡会导致用户的请求被分配到不同的服务器上&#xff0c;因此无法找到在…

IDEA实现NPM项目的自打包自发布自部署

目录 前言 正文 操作背景 NPM自发布 Package自发布 NPM部署 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Security and so on.&#x1f46f; I’m studying in University of Nottingham Ningbo China&#x1f…

射线和平面求交

射线和平面求交 1、平面方程 如果已知平面的高度&#xff08;即沿法向量方向的距离&#xff09;为 height&#xff0c;平面方程可以表示为&#xff1a; n ^ ⋅ p h e i g h t \bold{\hat{n}} \cdot p height n^⋅pheight p p p 是平面上的任意一点 height 的正负取决于法向量…

W外链创建抖音私信卡片教程,私信卡片跳转微信工具

W外链地址wai.cn 在数字化时代的浪潮中&#xff0c;私域流量的价值愈发凸显&#xff0c;成为企业获取用户、建立品牌忠诚度、提升转化率的关键手段。抖音&#xff0c;作为当下最热门的短视频社交平台之一&#xff0c;其用户基数庞大、互动性强&#xff0c;为企业私域引流提供了…

一些颜色的RGB整理

(214,219,233) (215,220,230) (189,189,189) (193,210,240) (190,210,240) (0,60,119) (0,60,120) (230,230,250)

百度地图基于范围的搜索

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、空间范围中点位数量的快速统计算法1.引入库2.百度地图基于范围的搜索 Java 前…

文件访问:C/C++/MFC

文章目录 1. C语言1.1 打开并读取文件1.2 写入文件1.3 读取二进制文件1.4 写入二进制文件1.5 文件指针的移动 2. C2.1 包含头文件2.2 打开文件2.3 检查文件是否成功打开2.4 读取文件2.5 写入文件2.6 关闭文件2.7 文件指针的移动和获取位置2.8 实例 3. C语言和C文件打开模式4. M…

分类题解清单

目录 简介MySQL题一、聚合函数二、排序和分组三、高级查询和连接四、子查询五、高级字符串函数 / 正则表达式 / 子句 算法题一、双指针二、滑动窗口三、模拟四、贪心五、矩阵六、排序七、链表八、设计九、前缀和十、哈希表十一、字符串十二、二叉树十三、二分查找十四、回溯十五…

初识Docker及管理Docker

Docker部署 初识DockerDocker是什么Docker的核心概念镜像容器仓库 容器优点容器在内核中支持2种重要技术&#xff1a;Docker容器与虚拟机的区别 安装Docker源码安装yum安装检查Docker Docker 镜像操作配置镜像加速器&#xff08;阿里系&#xff09;搜索镜像获取镜像查看镜像信息…

【LLMs】大语言模型分类

随着大语言模型&#xff08;LLM&#xff09;技术的发展&#xff0c;它们主要被划分为两个类别&#xff1a;基础LLM&#xff08;Base LLM&#xff09;和指令调优LLM&#xff08;Instruction-Tuned LLM&#xff09;。Base LLM侧重于广泛的语言理解和生成能力&#xff0c;而Instru…

计算机网络技术期末复习

一. 填空 在采用电信号表达数据的系统中&#xff0c;数据有 数字数据 和 模拟数据 两种。域名系统DNS是一个 分布式数据库 系统。TCP/IP的网络层最重要的协议是 IP互连网协议&#xff0c;它可将多个网络连成一个互连网。 4. 在TCP/IP层次模型的网络层中包括的协议主要有ARP 、…

@RequestPart和@RequestParam 区别和联系

目录 区别 联系 使用场景 示例 RequestParam 和 RequestPart 都是 Spring 框架中用于将 HTTP 请求中的数据绑定到控制器方法参数上的注解&#xff0c;但它们适用于不同的请求数据类型和场景。 区别 1.适用场景&#xff1a; RequestParam&#xff1a;主要用于处理简单类型…

科技出海|百分点科技智慧政务解决方案亮相非洲展会

近日&#xff0c;华为非洲全联接大会在南非约翰内斯堡举办&#xff0c;吸引政府官员行业专家、思想领袖、生态伙伴等2,000多人参会&#xff0c;百分点科技作为华为云生态合作伙伴&#xff0c;重点展示了智慧政务解决方案&#xff0c;发表《Enable a Smarter Government with Da…