java Server Sent Event 实现消息推送

news/2025/3/19 4:36:23/

我选择的是Server-sent events),简称SSE。主要是我理解起来简单。

这个链接是介绍 几种消息推送的方式java实现web实时消息推送的七种方案--个人学习记录_java实时推送前端数据_自不惘的博客-CSDN博客 

一、java服务端代码

//SSE:一种服务器发送事件(Server-sent events)
@Slf4j
public class SseEmitterUtil {//当前连接数private static AtomicInteger count = new AtomicInteger(0);//使用 map 对象,便于根据 userId 来获取对应的 SseEmitter,或者放 redis 里面private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();/*** 创建用户连接并返回 SseEmitter** @param userId 用户ID* @return SseEmitter*/public static SseEmitter connect(Integer userId) {//设置超时时间,0表示不过期。默认30秒,超过时间未完成会抛出异常:AsyncRequestTimeoutExceptionSseEmitter sseEmitter =  new SseEmitter(0l);try{//注册回调:完成、失败、超时sseEmitter.onCompletion(completionCallBack(userId));sseEmitter.onError(errorCallBack(userId));sseEmitter.onTimeout(timeoutCallBack(userId));// 缓存sseEmitterMap.put(String.valueOf(userId), sseEmitter);// 数量+1count.getAndIncrement();log.info("创建新的sse连接,当前用户:{}", userId);}catch (Exception e){log.info("创建新的sse连接异常,当前用户:{}", userId);}return sseEmitter;}/*** 给指定用户发送信息*/public static void sendMessage(Integer userId, String message) {if (!sseEmitterMap.containsKey(userId)) return;try {// sseEmitterMap.get(userId).send(message, MediaType.APPLICATION_JSON);sseEmitterMap.get(userId).send(message);} catch (IOException e) {log.error("用户[{}]推送异常:{}", userId, e.getMessage());removeUser(userId);}}/*** 移除用户连接*/public static void removeUser(Integer userId) {sseEmitterMap.remove(userId);// 数量-1count.getAndDecrement();log.info("移除用户:{}", userId);}/*** 获取当前连接数量*/public static int getUserCount() {return count.intValue();}private static Runnable completionCallBack(Integer userId) {return () -> {log.info("结束连接:{}", userId);removeUser(userId);};}private static Runnable timeoutCallBack(Integer userId) {return () -> {log.info("连接超时:{}", userId);removeUser(userId);};}private static Consumer<Throwable> errorCallBack(Integer userId) {return throwable -> {log.info("连接异常:{}", userId);removeUser(userId);};}
}

二、前端代码

目前uniapp不支持EventSource,暂时就是pc端

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>消息推送</title>
</head>
<body><div><button onclick="closeSse()">关闭连接</button><div id="message"></div></div>
</body><script>let source = null;// 用时间戳模拟登录用户const userId = new Date().getTime();if (window.EventSource) {// 建立连接source = new EventSource('http://localhost:8080/sse/connect/' + userId);/*** 连接一旦建立,就会触发open事件* 另一种写法:source.onopen = function (event) {}*/source.addEventListener('open', function (e) {setMessageInnerHTML("建立连接。。。");}, false);/*** 客户端收到服务器发来的数据* 另一种写法:source.onmessage = function (event) {}*/source.addEventListener('message', function (e) {setMessageInnerHTML(e.data);});/*** 如果发生通信错误(比如连接中断),就会触发error事件* 或者:* 另一种写法:source.onerror = function (event) {}*/source.addEventListener('error', function (e) {if (e.readyState === EventSource.CLOSED) {setMessageInnerHTML("连接关闭");} else {console.log(e);}}, false);} else {setMessageInnerHTML("你的浏览器不支持SSE");}// 监听窗口关闭事件,主动去关闭sse连接,如果服务端设置永不过期,浏览器关闭后手动清理服务端数据window.onbeforeunload = function () {closeSse();};// 关闭Sse连接function closeSse() {source.close();const httpRequest = new XMLHttpRequest();httpRequest.open('GET', 'http://localhost:8080/sse/close/' + userId, true);httpRequest.send();console.log("close");}// 将消息显示在网页上function setMessageInnerHTML(innerHTML) {document.getElementById('message').innerHTML += innerHTML + '<br/>';}
</script>
</html>


http://www.ppmy.cn/news/1070416.html

相关文章

医学影像PACS系统源码,患者登记、图像采集和处理、图像存储、报告产生的影像系统

PACS系统是医院影像科室中应用的一种系统&#xff0c;主要用于获取、传输、存档和处理医学影像。它通过各种接口&#xff0c;如模拟、DICOM和网络&#xff0c;以数字化的方式将各种医学影像&#xff0c;如核磁共振、CT扫描、超声波等保存起来&#xff0c;并在需要时能够快速调取…

Redis 7 教程 事务 过渡篇

理论 可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞 一个队列中,一次性、顺序性、排他性的执行一系列命令 Redis事务 VS 关系型数据库事务 单独的隔离操作Redis的事务仅仅是保证事务里的…

docker高级(redis集群三主三从)

1. 新建6个docker容器redis实例 docker run -d --name redis-node-1 --net host --privilegedtrue -v /redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381docker run -d --name redis-node-2 --net host --privilegedtrue -v /…

MYSQL 添加行号将行号写入到主键的列

MYSQL 添加行号 SELECT rownum: rownum 1 AS rownum, a.* FROM(SELECT rownum : 0) t,is_afxt.hk_vehicle a--或者(假设CREATED_TIME日期列数据不重复) select (select count(1)1 from is_afxt.hk_vehicle b where b.CREATED_TIME < a.CREATED_TIME) rownum ,a.* from i…

SpringBoot项目转为非Web项目

在微服务开发时&#xff0c;有时候某个服务可能并不需要是一个web项目&#xff0c;这时候应该怎么做呢&#xff1f; 去除pom中的web-starter 替换spring-boot-starter-web为spring-boot-starter&#xff0c;如果其他pom引入了web则需要逐一排除 <dependency><…

java自动登录 selenium 自动登录并获取cookie

选择操作网页 我用的edge&#xff0c;谷歌我的版本太高没有对应的驱动… 下载Edge的驱动程序&#xff0c;直接解压就好里面只有一个.exe文件 https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ 复制即用&#xff0c;看注释 import com.alibaba.fastjs…

【SpringMVC】@RequestMapping注解(详解)

文章目录 前言1、RequestMapping注解的功能2、RequestMapping注解的位置3、RequestMapping注解的value属性4、RequestMapping注解的method属性1、对于处理指定请求方式的控制器方法&#xff0c;SpringMVC中提供了RequestMapping的派生注解2、常用的请求方式有get&#xff0c;po…

深度学习基础篇 第二章: 转置卷积

参考教程&#xff1a; https://arxiv.org/pdf/1603.07285.pdf 文章目录 什么是转置卷积转置卷积的思想一维形式的理解二维形式的理解卷积和转置的关系no pading, unit stridespadding, unit stridesno padding, non-unit stridepadding&#xff0c;non-unit stride pytorch中的…