09. Springboot集成sse服务端推流

目录

1、前言

2、什么是SSE

2.1、技术原理

2.2、SSE和WebSocket

2.2.1、SSE (Server-Sent Events)

2.2.2、WebSocket

2.2.3、选择 SSE 还是 WebSocket?

3、Springboot快速集成

3.1、添加依赖

3.2、创建SSE控制器

3.2.1、SSEmitter创建实例

3.2.2、SSEmitter API

3.2.3、SSEmitter注册回调

4、小结


1、前言

如果项目中有一个场景,假设对接ChatGPT或对接天气类接口的时候,需要服务端主动往客户端进行消息推送或推流。通常的做法有:

  • 客户端提供接收数据接口,服务端开启定时轮询,定时向客户端发起http请求
  • 客户端提供定时轮询服务,定时向服务端发起http请求接口
  • 使用websocket实时通讯

那么今天再介绍另一种机制:SSE,也就是服务器发送事件机制。

2、什么是SSE

SSE(Server-Sent Events)是一种允许服务器向客户端推送实时数据的技术,它建立在 HTTP 和简单文本格式之上,提供了一种轻量级的服务器推送方式,通常也被称为“事件流”(Event Stream)。他通过在客户端和服务端之间建立一个长连接,并通过这条连接实现服务端和客户端的消息实时推送。

2.1、技术原理

SSE是建立在HTTP协议之上的,所以原理比较简单,也与HTTP原理类似:

1)建立连接:

客户端通过普通的 HTTP 请求向服务器发起连接请求,类似于普通的 Web 请求。这个请求的关键在于使用了 text/event-stream 的 MIME 类型,告知服务器该请求是 SSE 请求。

httpCopy codeGET /sse/stream HTTP/1.1
Host: example.com
Accept: text/event-stream

2)服务器处理请求:

服务器接收到 SSE 请求后,会在连接上保持打开状态,不会立即关闭。这是与普通的请求-响应模式的主要不同之处。服务器端通过这个持久连接向客户端发送数据。

3)数据推送:

服务器端通过打开的连接,周期性地向客户端发送消息。这些消息以文本的形式发送,并遵循一定的格式,通常以 data 字段表示消息内容。

httpCopy codeHTTP/1.1 200 OK
Content-Type: text/event-stream

data: This is a message\n\n

上述例子中,data 字段包含了实际的消息内容,两个换行符(\n\n)表示消息的结束。

4)客户端接收消息:

客户端通过监听连接的 message 事件来接收服务器推送的消息。一旦接收到消息,客户端可以采取相应的操作,例如更新界面内容。

javascriptCopy codeconst eventSource = new EventSource('/sse/stream');

eventSource.onmessage = function (event) {
    console.log('Received message:', event.data);
    // 处理消息,例如更新界面
};

5)连接关闭:

当服务器端不再需要向客户端推送消息时,或者发生错误时,服务器可以关闭连接。客户端也可以通过调用 eventSource.close() 来关闭连接。

2.2、SSE和WebSocket

提到SSE,那自然要提一下WebSocket了。WebSocket是一种HTML5提供的全双工通信协议(指可以在同一时间内允许两个设备之间进行双向发送和接收数据的通信协议),基于TCP协议,并复用HTTP的握手通道(允许一次TCP连接中传输多个HTTP请求和相应),常用于浏览器与服务器之间的实时通信。

SSE和WebSocket尽管功能类似,都是用来实现服务器向客户端实时推送数据的技术,但还是有一定区别:

2.2.1、SSE (Server-Sent Events)

  1. 简单性:SSE 使用简单的 HTTP 协议,通常建立在标准的 HTTP 或 HTTPS 连接之上。这使得它对于一些简单的实时通知场景非常适用,特别是对于服务器向客户端单向推送数据。
  2. 兼容性:SSE 在浏览器端具有较好的兼容性,因为它是基于标准的 HTTP 协议的。即使在一些不支持 WebSocket 的环境中,SSE 仍然可以被支持。
  3. 适用范围:SSE 适用于服务器向客户端单向推送通知,例如实时更新、事件通知等。但它仅支持从服务器到客户端的单向通信,客户端无法直接向服务器发送消息。

2.2.2、WebSocket

  1. 全双工通信: WebSocket 提供了全双工通信,允许客户端和服务器之间进行双向实时通信。这使得它适用于一些需要双向数据交换的应用,比如在线聊天、实时协作等。
  2. 低延迟:WebSocket 的通信开销相对较小,因为它使用单一的持久连接,而不像 SSE 需要不断地创建新的连接。这可以降低通信的延迟。
  3. 适用范围: WebSocket 适用于需要实时双向通信的应用,特别是对于那些需要低延迟、高频率消息交换的场景。

2.2.3、选择 SSE 还是 WebSocket?

  • 简单通知场景:如果你只需要服务器向客户端推送简单的通知、事件更新等,而不需要客户端与服务器进行双向通信,那么 SSE 是一个简单而有效的选择。
  • 双向通信场景:如果你的应用需要实现实时双向通信,例如在线聊天、协作编辑等,那么 WebSocket 是更合适的选择。
  • 兼容性考虑: 如果你的应用可能在一些不支持 WebSocket 的环境中运行,或者需要考虑到更广泛的浏览器兼容性,那么 SSE 可能是一个更可行的选择。

3、Springboot快速集成

3.1、添加依赖

Springboot项目中,sse不需要额外添加依赖,引用了web相关的springboot依赖即可:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

3.2、创建SSE控制器

这里简单创建一个控制器类,用于处理SSE请求。在JAVA中通常使用SSEmitter来实现sse的消息推送。

package com.example.springbootsse.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Date;


@RestController
@RequestMapping("/sse")
public class SSEmitterController {

    @GetMapping("/stream")
    public SseEmitter stream() {
        // 用于创建一个 SSE 连接对象
        SseEmitter emitter = new SseEmitter();

        // 在后台线程中模拟实时数据
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    // emitter.send() 方法向客户端发送消息
                    // 使用SseEmitter.event()创建一个事件对象,设置事件名称和数据
                    emitter.send(SseEmitter.event().name("message").data("[" + new Date() + "] Data #" + i));
                    Thread.sleep(1000);
                }
                // 数据发送完成后,关闭连接
                emitter.complete(); 
            } catch (IOException | InterruptedException e) {
                 // 发生错误时,关闭连接并报错
                emitter.completeWithError(e);
            }
        }).start();
        return emitter;
    }
}

查看执行结果,可以看到每一秒服务端都会自动像客户端推送messag消息:

我们来关注下SSEmitter这个类,SseEmitter 是 Spring Framework 中用于实现 Server-Sent Events(SSE)的一个类。它允许服务器向客户端推送数据,通过建立一个持久连接,实现服务器向客户端的实时单向通信。在 Spring 框架中,SseEmitter 类通常用于处理 SSE 请求,推送事件给客户端。

3.2.1、SSEmitter创建实例

SSEmitter提供了两个构造函数用于创建实例。在创建实例时,我们可以指定超时时间timeout,如果传0或使用无参构造,则表示永不过期。连接超时是指在一段时间内没有数据传输时,连接将被认为是超时的,并自动关闭。

3.2.2、SSEmitter API

除此以外,SSEmitter还提供了几种API,如上面例子中使用到的:

  1. emitter.send() 方法向客户端发送消息。
  2. SseEmitter.event() 创建一个事件对象,设置事件名称和数据。
  3. emitter.complete() 表示数据发送完成后关闭连接。
  4. emitter.completeWithError(e) 在发生错误时关闭连接并报错。

3.2.3、SSEmitter注册回调

SseEmitter 可以通过注册回调函数来处理服务器端发往客户端的事件。当服务器端有新的数据需要推送给客户端时,注册的回调函数将会被调用。SSEmitter继承了ResponseBodyEmitter,提供的一系列注册回调函数有:

示例代码:

package com.example.springbootsse.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Date;


@RestController
@RequestMapping("/sse")
public class SSEmitterController {

    @GetMapping("/stream")
    public SseEmitter stream() {
        // 3S超时
        SseEmitter emitter = new SseEmitter(10000L);

        // 注册回调函数,处理服务器向客户端推送的消息
        emitter.onCompletion(() -> {
            System.out.println("Connection completed");
            // 在连接完成时执行一些清理工作
        });

        emitter.onTimeout(() -> {
            System.out.println("Connection timeout");
            // 在连接超时时执行一些处理
            emitter.complete();
        });

        // 在后台线程中模拟实时数据
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    emitter.send(SseEmitter.event().name("message").data("[" + new Date() + "] Data #" + i));
                    Thread.sleep(1000);
                }
                emitter.complete(); // 数据发送完成后,关闭连接
            } catch (IOException | InterruptedException e) {
                emitter.completeWithError(e); // 发生错误时,关闭连接并报错
            }
        }).start();

        return emitter;
    }
}
  1. onCompletion():在连接完成时候触发,可在连接完成时执行一些清理工作
  2. onTimeout():当连接超时时触发
  3. onError():当连接异常时触发
  4. completeWithError(e):用于发生错误时,关闭连接并报错

4、小结

其实SSE已经出来很久了,但是熟知他的人却很少,大多数项目中还是直接使用了websocket技术。直到最近ChatGPT火了之后,很多项目需要对接GPT进行实时推流,才逐渐又被人提起。所以借此篇文章给自己扫盲一下。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

K8s-持久化(持久卷,卷申明,StorageClass,StatefulSet持久化)

POD 卷挂载 apiVersion: v1 kind: Pod metadata:name: random-number spec:containers:- image: alpinename: alpinecommand: ["/bin/sh","-c"]args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"]volumeMounts:- mountPath: /optname: da…

Ubuntu findfont: Font family ‘SimHei‘ not found.

matplotlib中文乱码显示 当我们遇到这样奇怪的问题时, 结果往往很搞笑 尝试1不行 Stopping Jupyter Installing font-manager: sudo apt install font-manager Cleaning the matplotlib cache directory: rm ~/.cache/matplotlib -fr Restarting Jupyter. 尝试2 This work fo…

AI大模型开发架构设计(6)——AIGC时代,如何求职、转型与选择?

文章目录 AIGC时代&#xff0c;如何求职、转型与选择&#xff1f;1 新职场&#xff0c;普通人最值钱的能力是什么?2 新职场成长的3点建议第1点&#xff1a;目标感第2点&#xff1a;执行力第3点&#xff1a;高效生产力 3 新职场会产生哪些新岗位机会?如何借势?4 新职场普通人…

微信小程序(十七)自定义组件生命周期(根据状态栏自适配)

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.获取手机状态栏的高度 2.验证attached可以修改数据 3.动态绑定样式数值 源码&#xff1a; myNav.js Component({lifetimes:{//相当于vue的created,因为无法更新数据被打入冷宫created(){},//相当于vue的mount…

【运行Python爬虫脚本示例】

主要内容&#xff1a;Python中的两个库的使用。 1、requests库&#xff1a;访问和获取网页内容&#xff0c; 2、beautifulsoup4库&#xff1a;解析网页内容。 一 python 爬取数据 1 使用requests库发送GET请求&#xff0c;并使用text属性获取网页内容。 然后可以对获取的网页…

基于python flask茶叶网站数据大屏设计与实现,可以做期末课程设计或者毕业设计

基于Python的茶叶网站数据大屏设计与实现是一个适合期末课程设计或毕业设计的项目。该项目旨在利用Python技术和数据可视化方法&#xff0c;设计和开发一个针对茶叶行业的数据大屏&#xff0c;用于展示和分析茶叶网站的相关数据。 项目背景 随着互联网的快速发展&#xff0c;越…

一张图区分Spring Task的3种模式

是的&#xff0c;只有一张图&#xff1a; fixedDelay 模式cron 模式fixedRate 模式

[HUBUCTF 2022 新生赛]ezPython

打开是一个pyc文件&#xff0c;我用的是pycdc进行反编译 # Source Generated with Decompyle # File: ezPython.pyc (Python 3.7)from Crypto.Util.number import * import base64 import base58 password open(password.txt, r).read() tmp bytes_to_long(password.encode(u…

基于JavaWeb开发的家具定制购买网站【附源码】

基于JavaWeb开发的家具定制购买网站【附源码】 &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制系统 &#…

GLog开源库使用

Glog地址&#xff1a;https://github.com/google/glog 官方文档&#xff1a;http://google-glog.googlecode.com/svn/trunk/doc/glog.html 1.利用CMake进行编译&#xff0c;生成VS解决方案 &#xff08;1&#xff09;在glog-master文件夹内新建一个build文件夹&#xff0c;用…

docker-compose Install influxdb1+influxdb2+telegraf

influxd2前言 influxd2 是 InfluxDB 2.x 版本的后台进程,是一个开源的时序数据库平台,用于存储、查询和可视化时间序列数据。它提供了一个强大的查询语言和 API,可以快速而轻松地处理大量的高性能时序数据。 telegraf 是一个开源的代理程序,它可以收集、处理和传输各种不…

在Ubuntu上安装pycuda记录

1. 安装CUDA Toolkit 11.8 从MZ小师妹的摸索过程来看&#xff0c;其他版本的会有bug&#xff0c;12.0的版本太高&#xff0c;11.5的太低&#xff08;感谢小师妹让我少走弯路&#xff09; 参考网址&#xff1a;CUDA Toolkit 11.8 Downloads | NVIDIA Developer 在命令行输入命…

调用阿里通义千问大语言模型API-小白新手教程-python

阿里大语言模型通义千问API使用新手教程 最近需要用到大模型&#xff0c;了解到目前国产大模型中&#xff0c;阿里的通义千问有比较详细的SDK文档可进行二次开发,目前通义千问的API文档其实是可以进行精简然后学习的,也就是说&#xff0c;是可以通过简单的API调用在自己网页或…

Vue<圆形旋转菜单栏效果>

效果图&#xff1a; 大家不一定非要制成菜单栏&#xff0c;可以看下人家的华丽效果&#x1f61d;&#xff0c;参考地址 https://travelshift.com/ 大佬写的效果可比我的强多了&#xff0c;但是无从下手&#xff0c;所以就自己琢磨怎么写了&#xff0c;只能说效果勉强差不多 可…

“steam教学理念”scratch+数学 ——时钟案例

一、时钟概念 它通常由一个圆形表盘组成&#xff0c;表盘上有12个数字&#xff0c;分别是1到12。这些数字代表了小时。在表盘上&#xff0c;还有三根指针&#xff0c;一根较短的指针叫做时针&#xff0c;另一根较长的指针叫做分针&#xff0c;而秒针通常为红色&#xff0c;且指…

LabVIEW电缆检修系统

在电力系统中&#xff0c;合理选择电缆检修策略是保障电网稳定运行的关键。现有的电缆检修策略往往忽视了电缆的技术和经济双重指标&#xff0c;导致检修效率低下和维护成本高昂。为此&#xff0c;开发了一种基于风险评估模型和全寿命周期成本&#xff08;LCC&#xff09;的电缆…

java金额数字转中文

java金额数字转中文 运行结果&#xff1a; 会进行金额的四舍五入。 工具类源代码&#xff1a; /*** 金额数字转为中文*/ public class NumberToCN {/*** 汉语中数字大写*/private static final String[] CN_UPPER_NUMBER {"零", "壹", "贰",…

Springboot+Netty搭建基于TCP协议的服务端

文章目录 概要pom依赖Netty的server服务端类Netty通道初始化I/O数据读写处理测试发送消息 并 接收服务端回复异步启动Netty运行截图 概要 Netty是业界最流行的nio框架之一&#xff0c;它具有功能强大、性能优异、可定制性和可扩展性的优点 Netty的优点&#xff1a; 1.API使用简…

高中数学常识

一、大小关系 |x| > |sinx| 理由&#xff1a; 很明显&#xff0c;在圆内&#xff0c;弧长x>垂线sinx 3x、2x 、 1 2 \frac{1}{2} 21​x 理由&#xff1a; log 1 2 _\frac{1}{2} 21​​x、log 2 _2 2​x、 log 3 _3 3​x 二、(xy)? 的求法 利用二项式定理 三、平…

GitHub国内打不开(解决办法有效)

最近国内访问github.com经常打不开&#xff0c;无法访问。 github网站打不开的解决方法 1.打开网站http://tool.chinaz.com/dns/ &#xff0c;在A类型的查询中输入 github.com&#xff0c;找出最快的IP地址。 2.修改hosts文件。 在hosts文件中添加&#xff1a; # localhost n…