写点什么

原来 Spring 可以这样推送消息到客户端!

作者:架构师之道
  • 2024-12-29
    湖南
  • 本文字数:2190 字

    阅读完需:约 7 分钟

Spring 框架中可以使用 SseEmitter 实现服务端向客户端推送消息,今天盘一盘。

SseEmitter 简介

SseEmitter 是 Spring Framework 中用于服务器发送事件(Server-Sent Events, SSE)的类。SSE 是一种允许服务器主动向客户端推送数据的技术,通常用于实现如实时通知、数据流等功能。其工作原理基于 HTTP 协议,客户端通过 HTTP 请求订阅服务器的事件流,服务器则通过 SseEmitter 对象持续向客户端发送事件。

SseEmitter 的核心特性

  • 单向通信:SSE 是单向的,只允许服务器向客户端推送数据,客户端不能通过此通道向服务器发送数据。

  • 基于 HTTP:SSE 使用标准的 HTTP 协议,因此不需要额外的协议支持,易于实现和使用。

  • 自动重连:如果连接中断,客户端浏览器会自动尝试重新连接服务器。

  • 事件流格式:SSE 使用简单的文本格式传输数据,每条消息以 data:开头,并以两个换行符\n\n 结束

SseEmitter 的使用场景

  • 实时数据流:在需要实时数据更新的应用中,如股票交易平台,服务器可以使用 SseEmitter 向客户端推送最新的股票价格和交易信息。

  • 实时通知:适用于需要向用户发送实时通知的场景,例如在线教育平台的新课程发布或作业提交通知。

  • 聊天应用:在聊天应用中,SseEmitter 可以用来实现实时消息推送,提高消息传递的效率。

  • 监控系统:在监控系统中,SseEmitter 可以用于实时推送监控数据,如物联网项目中传感器采集的数据,以实时监控设备状态。

SseEmitter 的基本用法

  • 创建 SseEmitter 实例:在控制器中创建一个 SseEmitter 实例,并将其返回给客户端。

  • 发送事件:通过 SseEmitter 实例的 send 方法向客户端发送事件。

  • 处理连接关闭:通过 SseEmitter 的 onCompletion 和 onTimeout 方法处理连接关闭或超时的情况。

  • 通过 SseEmitter,开发者可以构建出高效、可靠的实时应用,提升用户体验和系统性能。

下面我们通过一个简单示例演示下

前端服务

使用 Vue CLI 创建一个 Vue 3 项目,

安装 Vue CLI

npm install -g @vue/cli
复制代码

创建 Vue 3 项目

vue create my-vue3-app
复制代码

启动服务

cd my-vue3-appnpm run serve
复制代码

创建一个 vue 文件 EventList.vue

获取消息,然后进行等待服务端的消息。

<template>  <div>    <h1>Server-Sent Events</h1>    <ul>      <li v-for="message in messages" :key="message.id">{{ message.content }}</li>    </ul>  </div></template>
<script setup>import { ref, onMounted, onUnmounted } from 'vue';import { SseClient } from './SseClient';

const messages = ref([]);
const sseClient = new SseClient((event) => { const data = JSON.parse(event.data); messages.value.push(data);});
onMounted(() => { sseClient.connect();});
onUnmounted(() => { sseClient.disconnect();});</script>
<style scoped>/* Add your styles here */</style>
复制代码

创建 JS 文件 SseClient

export class SseClient {    constructor(onMessage) {      this.onMessage = onMessage;      this.eventSource = null;    }      connect() {      this.eventSource = new EventSource('http://localhost:8089/events/54321');      this.eventSource.onmessage = (event) => {        this.onMessage(event);      };      this.eventSource.onerror = (error) => {        this.disconnect();      };    }      disconnect() {      if (this.eventSource) {        this.eventSource.close();        this.eventSource = null;      }    }  }
复制代码

然后再 Vue 项目入口 APP.vue 中引入刚才的 Vue 文件。

后端服务

创建 SpringBoot 服务

直接写一个简易的 Controller,将 SseEmitter 缓存起来,可以直接进行消息发送。

@Controllerpublic class SseEmitterController {    private static Map<String, SseEmitter> sseCache = new ConcurrentHashMap<>();
@CrossOrigin(origins = "*") @GetMapping("/events/{userId}") public SseEmitter stream(@PathVariable String userId) throws IOException { sseCache.put(userId,new SseEmitter(10 * 60 * 1000L)); SseEmitter emitter = sseCache.get(userId); Map<String,String> map = new HashMap<>(); map.put("id", userId); map.put("content", "连接成功"); emitter.send(map); return emitter; }
@GetMapping("/sendMessage/{userId}") public String sendMessage(@PathVariable String userId, @RequestParam(name = "message", required = false) String message) throws IOException { SseEmitter emitter = sseCache.get(userId); Map<String,String> map = new HashMap<>(); map.put("id", userId); map.put("content", message); emitter.send(map); return "成功"; }
@GetMapping("/close/{userId}") public String close(@PathVariable String userId ) throws IOException { SseEmitter emitter = sseCache.get(userId); emitter.complete(); return "成功"; }}
复制代码

前后端启动后联调:

初始化连接成功。



再手动发送消息。



客户端接收消息:



以上是一个简易的服务端主动推送消息给客户端的例子。


来源:https://mp.weixin.qq.com/s/Flf0B1U5vUQ2nzCp8zv-6Q

用户头像

还未添加个人签名 2022-04-10 加入

还未添加个人简介

评论

发布
暂无评论
原来Spring可以这样推送消息到客户端!_Java_架构师之道_InfoQ写作社区