Server-Sent Events向前端消息推送技术
创作时间:
作者:
@小白创作中心
Server-Sent Events向前端消息推送技术
引用
简书
1.
https://www.jianshu.com/p/942e1b5223b3
什么是SSE技术
Server-Sent Events (SSE) 是一种用于服务端向客户端单向实时通信的Web技术。它适用于消息通知的场景,能够实现实时数据推送。
SSE技术优缺点
优点
- 实时性
- SSE 一般只用来传送文本,二进制数据需要编码后传送
- SSE 使用 HTTP 协议,只是content-type标识数据类型为
text/event-stream
缺点
- 单向通信
- 老旧浏览器无法兼容
- 无法跨域
SSE通信本质
- 前端向后端发起连接请求,建立连接后会一直保持连接,后端会一直发送数据流到前端。前端关闭连接,就断开连接。
- 前端添加事件监听。
- 前端就可以接收到后端消息推送。
Spring Boot实现SSE技术
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.13</version>
</dependency>
代码
/**
* 消息模型
*/
public class MessageInfo {
private String userNo;
private String message;
private String sendTime;
}
/**
* SSE服务实现
*/
public class SseEmitterServer {
/**
* 统计SSE在线连接数
*/
private static AtomicInteger count = new AtomicInteger(0);
/**
* 客户标识符和消息推送映射关系
*/
private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
/**
* 建立连接
* @param userNo
* @return
*/
public static SseEmitter connect(String userNo) {
// 设置超时日期,0表示不过期
SseEmitter sseEmitter = new SseEmitter(0L);
sseEmitter.onCompletion(() -> {
log.info("结束连接 userNo = " + userNo);
});
sseEmitter.onError(throwable -> {
log.info("连接异常 userNo = " + userNo);
removeUser(userNo);
});
sseEmitter.onTimeout(() -> {
log.info("连接超时 userNo = " + userNo);
removeUser(userNo);
});
sseEmitterMap.put(userNo, sseEmitter);
count.getAndIncrement();
log.info("创建新SSE连接,userNo = " + userNo);
return sseEmitter;
}
/**
* 移出用户
* @param userNo
*/
public static void removeUser(String userNo){
sseEmitterMap.remove(userNo);
count.getAndDecrement();
log.info("移除的 userNo = " + userNo);
log.info("剩余的 userNo集合 = " + sseEmitterMap.keySet());
}
/**
* 向指定的userNo发送消息message
* @param userNo
* @param message
*/
public static void sendMessageToUserNo(String userNo, String message) {
if (!sseEmitterMap.containsKey(userNo)) {
connect(userNo);
}
try {
sseEmitterMap.get(userNo).send(message);
log.info("向 userNo = " + userNo + "发送消息,内容如下:" + message);
} catch (IOException e) {
log.error("向 userNo = " + userNo + "发送消息出错,错误内容如下:" + e.getMessage());
e.printStackTrace();
}
}
}
@Controller
@RequestMapping("/sse")
public class SseController {
/**
* 接收连接
* @param userNo
* @return
*/
@GetMapping("/connect/{userNo}")
public SseEmitter connect( String userNo){
SseEmitter sseEmitter = SseEmitterServer.connect(userNo);
return sseEmitter;
}
/**
* 关闭连接
* @param userNo
* @return
*/
@GetMapping("/close/{userNo}")
public String closeSse( String userNo) {
SseEmitterServer.removeUser(userNo);
return "关闭 userNo = " + userNo + "连接";
}
/**
* 发送消息
* @param userNo
* @param messageInfo
* @return
*/
@PostMapping("/send/{userNo}")
public String sendMessageToUserNo( String userNo, @RequestBody MessageInfo messageInfo) {
if (Objects.isNull(messageInfo) || StrUtil.isBlank(messageInfo.getMessage())) {
log.error("消息内容不能为空!");
return "消息内容不能为空!";
}
messageInfo.setUserNo(userNo);
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String nowTime = LocalDateTime.now().format(dtf);
messageInfo.setSendTime(nowTime);
// 向SSE前端发送消息
String jsonStr = JSONUtil.toJsonStr(messageInfo);
SseEmitterServer.sendMessageToUserNo(userNo, jsonStr);
return "向 userNo = " + userNo + "发送消息,内容为:" + jsonStr;
}
}
前端实现SSE技术
前端封装sse的js代码:
let eventSource = null;
export function createConnection() {
let url = "http://localhost:3831/sse/connect/123456";
let headers = {};
eventSource = new EventSource(url, headers);
}
export function addListener() {
eventSource.onopen = () => {
console.log("eventSource.onopen 连接畅通");
}
eventSource.onmessage = (event) => {
console.log("eventSource.onmessage 收到消息 event.data = " + event.data);
eventSource.close();
console.log("收到消息后关闭连接");
}
eventSource.onerror = (err) => {
console.log("eventSource.onerror 发生错误 err = " + err);
}
}
前端页面代码:
<template>
<div class="hello">
<el-row style="margin-top:10px;">
<el-col :span="12">
<el-button type="primary" @click="openAndRecvForSSE">点击</el-button>
</el-col>
</el-row>
</div>
</template>
<script>
import {createConnection, addListener} from "@/utils/sse.js";
export default {
name: 'HelloWorld',
data () {
return {
}
},
watch: {},
mounted() {},
created() { },
methods: {
openAndRecvForSSE() {
createConnection();
addListener();
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>
测试结果
- 点击按钮,建立连接,前端添加监听事件,后端与前端成功建立连接。
- 使用postman发送消息给Controller,其中使用SSE消息通知到前端。
抓包查看SSE
参考
热门推荐
青海湖摄影攻略:三大主题拍摄技巧详解
汽车选购攻略:如何选择最适合自己的车型
《老年听力健康核心信息》发布,如何帮他们把耳朵“叫醒”?
健康科普丨遇上甲流不要怕,教你如何科学防护
广东省名中医推荐:核桃莲子猪心汤
抖音美食推荐官教你做莲子猪心汤!
秋冬养生佳品:猪心莲子汤的制作与养生之道
舒筋活血片:缓解疼痛的常用中成药
临江仙杨慎:词人的生平与传世之作
探索当代佛教、儒教、道教的精神智慧
谷养康推荐:黄芪泡酒的正确打开方式
小尾翼改装新规出炉!你准备好了吗?
速锐车主DIY加装小尾翼教程
汽车尾翼改装全攻略:从选购到安装
汽车尾翼改装,这些坑你踩过吗?
F1赛车尾翼:从简单机翼到科技巅峰的进化之路
血糖稳定:探索糖尿病患者必备的8种优质蛋白食品
【科普营养】糖尿病最友好的食物——膳食纤维
肿瘤专家:放疗期间可用中药吗?中医如何认识化疗并治疗?
刚察气象站揭示:青海湖近40年气候变化真相
青海湖冰封奇观:你见过吗?
冬日青海湖:蓝冰、日出与星空的绝美邂逅
青海湖蓝冰奇观:冬日打卡圣地
5分钟告诉你,买车该选小型车还是大型车!
最新研究揭秘:快速提高认知能力的科学方法
世界读书日必读:《思考,快与慢》
朱利安·巴吉尼新书教你如何提高认知
认知行为疗法:如何重塑你的思维?
王菲春晚泪目背后的家国情怀
慈禧太后掌权之路的探讨