服务端实时推送技术之SSE(Server-Send Events)

news2024/11/29 1:38:09

文章目录

  • 前言
  • 一、解决方案:
    • 1、传统实时处理方案:
    • 2、HTML5 标准引入的实时处理方案:
    • 3、第三方推送:
  • 二、SSE:
    • 1、客户端:
    • 2、服务端:
  • 三、业务实践:
  • 总结:


前言

服务端推送,也称为消息推送或通知推送,是一种允许应用服务器主动将信息发送到客户端的能力,为客户端提供了实时的信息更新和通知,增强了用户体验。

服务端推送的背景与需求主要基于以下几个诉求:

  1. 实时通知:在很多情况下,用户期望实时接收到应用的通知,如新消息提醒、商品活动提醒等。

  2. 节省资源:如果没有服务端推送,客户端需要通过轮询的方式来获取新信息,会造成客户端、服务端的资源损耗。通过服务端推送,客户端只需要在收到通知时做出响应,大大减少了资源的消耗。

  3. 增强用户体验:通过服务端推送,应用可以针对特定用户或用户群发送有针对性的内容,如优惠活动、个性化推荐等。这有助于提高用户对应用的满意度和黏性。

常见推送场景有:微信消息通知栏、新闻推送、外卖状态 等等,我们自身的推送场景有:下载、连线请求、直播提醒 …

一、解决方案:

1、传统实时处理方案:

轮询:这是一种较为传统的方式,客户端会定时地向服务端发送请求,询问是否有新数据。服务端只需要检查数据状态,然后将结果返回给客户端。轮询的优点是实现简单,兼容性好;缺点是可能产生较大的延迟,且对服务端资源消耗较高。

长轮询(Long Polling):轮询的改进版。客户端向服务器发送请求,服务器收到请求后,如果有新的数据,立即返回给客户端;如果没有新数据,服务器会等待一定时间(比如30秒超时时间),在这段时间内,如果有新数据,就返回给客户端,否则返回空数据。客户端处理完服务器返回的响应后,再次发起新的请求,如此反复。长轮询相较于传统的轮询方式减少了请求次数,但仍然存在一定的延迟。

2、HTML5 标准引入的实时处理方案:

WebSocket:一种双向通信协议,同时支持服务端和客户端之间的实时交互。WebSocket 是基于 TCP 的长连接,和HTTP 协议相比,它能实现轻量级的、低延迟的数据传输,非常适合实时通信场景,主要用于交互性强的双向通信。

SSE:SSE(Server-Sent Events)是一种基于 HTTP 协议的推送技术。服务端可以使用 SSE 来向客户端推送数据,但客户端不能通过SSE向服务端发送数据。相较于 WebSocket,SSE 更简单、更轻量级,但只能实现单向通信。

两者的主要区别:

在这里插入图片描述

3、第三方推送:

常见的有操作系统提供相应的推送服务,如苹果的APNs(Apple Push Notification service)、谷歌的FCM(Firebase Cloud Messaging)等。同时,也有一些跨平台的推送服务,如个推、极光推送、友盟推送等,帮助开发者在不同平台上实现统一的推送功能。

这种推送方式在生活中十分常见,一般你打开手机就能看到各种信息推送,基本就是利用第三方推送来实现。

二、SSE:

接下来我们重点讲讲 SSE 服务端推送,它基于 HTTP 协议,易于实现和部署,特别适合那些需要服务器主动推送信息、客户端只需接收数据的场景:

在这里插入图片描述

1、客户端:

Server-Sent Events(SSE)是 HTML5 的一部分,用于从服务器实时接收更新,目前大部分主流浏览器都提供了支持:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSE Client</title>
</head>
<body>
    <h1>Receive: <span id="sse"></span></h1>
    <script>
        const numberElement = document.getElementById("sse");
        const source = new EventSource('http://localhost:8080/sse');

        source.onmessage = (event) => {
            numberElement.innerText = event.data;
        };

        source.onerror = (error) => {
            console.error("SSE error:", error);
        };
    </script>
</body>
</html>

自动重连:一旦连接断开,浏览器会自动尝试重新建立连接。当然,每个浏览器都有自己的重连策略和措施。因此,重连时间和尝试次数可能因浏览器而异。

也可以通过 CURL 调用:

➜  ~ curl -X GET -H  "Accept:text/event-stream" -H  "Content-Type:application/x-www-form-urlencoded" "http://localhost:8080/sse"
data:Data: 0
data:Data: 1
data:Data: 2
data:Data: 3
...

2、服务端:

我们目前服务端主要使用 Spring,其对 SSE 主要提供了两种支持:

  • Spring WebMVC:传统的基于 Servlet 的同步阻塞编程模型,即 同步模型Web框架。
  • Spring WebFlux:异步非阻塞的响应式编程模型,即 异步模型Web框架。

1)Spring WebFlux 中的 SSE 支持(支持版本Spring5.0):

Spring WebFlux 框架提供了一套基于响应式编程的非阻塞异步IO模型,能高效支持 SSE。在 Spring WebFlux 中,我们可以结合 Flux 和 MediaType.TEXT_EVENT_STREAM_VALUE 来实现 SSE。

以下示例展示如何在 Spring WebFlux 中创建 SSE 流:

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.time.Duration;

@RestController
public class SseController {

    @GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> getSseStream() {
        // 使用Flux生成每秒一个递增的数据流,用于模拟实时数据推送
        return Flux.interval(Duration.ofSeconds(1))
                .map(sequence -> "Data: " + sequence);
    }
}

Spring WebFlux 底层依赖于两个非阻塞的异步框架: Reactor 和 Netty。其中,Reactor 库主要是提供响应式式编程的支持,Netty 是一个高性能的非阻塞网络框架,主要负责处理 HTTP 输入输出。

  • Reactor:Reactor 是一个基于 Java 8 的响应式流库,它实现了 Reactive Streams 规范。Reactor 提供了两个核心的响应式类型 - Mono 和 Flux,相当于 findOne 和 findList 的区别。这两个类型提供了丰富的操作符,允许你以声明式和函数式的方式来处理你的业务逻辑。
  • Netty:Netty 是一个高性能、异步的事件驱动的网络框架,虽然 Netty 重点在网络通信层,但仍然提供了 Web 服务器的能力。
    Reactor 对 Netty 进行了集成,提供了子模块 Reactor Netty,在 Spring WebFlux 中,Reactor Netty 主要用作默认的服务器运行时环境,负责处理 HTTP 请求和响应。

这两个框架共同为 Spring WebFlux 提供了底层的支持,使得我们能够使用响应式编程编写高性能、可扩展的Web应用程序。另外,虽然 Spring WebFlux 在底层默认使用 Reactor 和 Netty,但它也有很好的灵活性和可替换性,我们可以根据需要更换其他非阻塞的异步框架。

2)Spring WebMVC 中的 SSE 支持(支持版本Spring4.2):

在 Spring WebMVC 中,可以通过SseEmitter对象来处理 SSE 请求,它允许将数据通过 SSE 连接发送给客户端。

下面是一个使用SseEmitter提供 SSE 的简单示例:

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

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
public class SseController {

    private ExecutorService nonBlockingService = Executors.newCachedThreadPool();

    @GetMapping("/sse")
    public SseEmitter getSseStream() {
        SseEmitter emitter = new SseEmitter();

        nonBlockingService.execute(() -> {
            // 这里模拟数据发送给客户端的逻辑
            try {
                for (int i = 0; i < 10; i++) {
                    emitter.send("Data: " + i);
                    Thread.sleep(1000);
                }
                emitter.complete();
            } catch (Exception ex) {
                emitter.completeWithError(ex);
            }
        });

        return emitter;
    }
}

如果你的项目使用 SpringMVC 模型,不想再引入 Spring WebFlux,能否利用 Reactor 响应式库呢?

答案也是可以的,虽然 SpringMVC 主要提供的是同步阻塞能力,但也不妨碍它提供一定的异步支持,比如这里,我们可以直接引入 Reactor 库,也同样可以实现 SSE:

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.time.Duration;

@RestController
public class SseController {

    @GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> getSseStream() {
        // 使用Flux生成每秒一个递增的数据流,用于模拟实时数据推送
        return Flux.interval(Duration.ofSeconds(1))
                .map(sequence -> "Data: " + sequence);
    }
}

基于 Spring WebMVC 或 Spring WebFlux,我们可以方便地在 Spring 框架中实现 SSE 的支持。这两种方法根据具体需求和场景,可以灵活选择。

三、业务实践:

我们以「文件下载」功能进行说明,一般情况下,大文件的下载,服务端压力比较大、处理时间也比较长,为了有更好的交互体验,我们可以使用异步处理,服务端处理完了之后主动通知 客户端,效果如下:

在这里插入图片描述

这个小弹窗就是服务端处理完了之后,通过 SSE连接主动推送到客户端。

我们来看看处理流程:

在这里插入图片描述

1)SSE 连接:

先建立 SSE 连接,确保服务端有主动推送消息的能力。

2)异步下载:

长耗时下载任务我们通过异步的方式处理,避免用户在下载页面长时间等待。

3)广播并推送:

下载完成后,我们需要将完成事件推送给客户端。需要注意的是,由于服务是集群部署、SSE 连接在节点本地 Map 维护,这就有可能导致当前客户端的 SSE连接所在节点 与 事件推送节点 是两个独立的节点。

因此,我们这里借助于 Redis 的发布/订阅能力,将消息广播出去,能匹配连接的节点负责将消息推送至客户端、其他节点直接丢弃即可。效果图如下:

在这里插入图片描述

能否做到精准投递?

答案也是可以的,我们可以这样来做:

  • 借助 Redis 做中心存储,存储Map<用户,节点IP> 这样的映射关系。
  • 在推送消息之前,先通过映射关系找到该用户的SSE连接所在节点
  • 然后在通过 RPC调用 直接将消息投递到对应的服务节点,最后由该节点进行事件推送。

一般情况下,我们可以用「广播」这种简单粗暴的方式应对大部分场景,毕竟「精准投递」需要中心化的维护节点关系、应对节点变更等,处理起来稍显麻烦。具体视业务场景来做选择即可。

总结:

SSE 技术是一种轻量级的实时推送技术,具有支持跨域、使用简单、支持自动重连等特点,使得其在实时消息推送、股票交易等场景下广泛使用。

另外,SSE 相对于 WebSocket 更加轻量级,如果需求场景不需要交互式动作,那么 SSE 是一个不错的选择。

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

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

相关文章

JavaWeb之过滤器Filter

今天开发遇到了&#xff0c;简单记录一下&#xff01; 简介&#xff1a;Filter是JavaWeb三大组件之一&#xff08;Servlet程序、Listener监听器、Filter过滤器&#xff09; 作用&#xff1a;既可以对请求进行拦截&#xff0c;也可以对响应进行处理。 1、Filter中的三个方法 …

人类 vs AI:玩梗大作战,看看谁是最后的赢家?

能解释人类玩梗的 AI 究竟能多大程度地理解人类的「梗」&#xff1f; 五一假期就在眼前&#xff0c;LigaAI 小编每天都在「调休好烦」和「快放假啦」两种情绪间反复横跳&#xff0c;还会忍不住思考「AI 能不能理解调休和放假的情绪差异&#xff1f;」&#xff08;一些精神世界高…

xilinx block design address editor 计算

xilinx block design address editor 计算 1k 0x000 ~ 0x3ff 10bit 1m 00000 ~ FFFFF 20bit 每个pcie 配置空间有4k 【11:0】 PCIe 配置空间 (PCIe Configuration Space) PCIe Spec中定义&#xff1a;每个PCIe Function都有 4096 Byte 的配置空间(Configuration Space)。前256…

基于机器学习的纠错系统技术 - 智能文本纠错 API

引言 在过去的几十年里&#xff0c;文本纠错技术已经取得了巨大的进展&#xff0c;从最初的基于规则的纠错系统到现在的基于机器学习的纠错系统&#xff0c;技术的发展已经帮助人们解决了大量的文本纠错问题&#xff0c;随着机器学习技术的发展&#xff0c;文本纠错技术也发生…

WINCC 趋势判断

项目函数 //状态: //稳定 2 //稳定 //递增 -1 //无序 0 //波动 //递减 1 int ordered(double *Trend_data, int Trend_len) {int Trend_out 0,Order 0;if (Trend_len 1){return 2;}Trend_out (Trend_data[0] < Trend_data[1]) - (Trend_data[0] > Trend_da…

input 各类事件汇总触发时机触发顺序

今天梳理了一下input框的各类事件&#xff0c;简单介绍一下吧 目录 1.click 2.focus 3.blur 4.change 5.input 6.keydown 7.keyup 8.select 1.click 点击事件&#xff0c;简单易理解&#xff0c;点击触发&#xff0c;等下跟focus事件一起比较 2.focus 获取焦点事件…

新能源行业雨水除铊,污水中铊超标的解决方法

锂电行业的发展会带动相关产业的发展&#xff0c;例如锂电池原材料和生产设备的制造、电池回收和处理等&#xff0c;这些产业的发展可能也会带来铊排放问题。除了锂电池生产过程中可能存在的铊污染外&#xff0c;企业的生活污水也可能含有铊&#xff0c;因为铊是一种广泛存在于…

Java面试题总结 | Java面试题总结6-MYSQL模块(持续更新)

Mysql 文章目录 Mysql关系型数据库和非关系型数据库的区别什么是ORM?-**mybatis**如何评估一个索引创建的是否合理&#xff1f;Count函数执行效果上&#xff1a;执行效率上&#xff1a;count(主键)和count(列名) 数据库的三大范式Mysql中char和varchar的区别数据库设计或者功能…

【科普知识】电机的10种工作制说明:S1~S10

如今&#xff0c;在我们的生活中&#xff0c;电机几乎无处不在&#xff0c;从国防、工农、运输、临床器械、通讯到生活中的洗衣机、风扇、吸尘器、电动机器人等&#xff0c;都在应用着各式各样的电动机。 电机作为一种能够将电能转换成机械能的装置&#xff0c;是现代工业生产和…

Elisp之检测函数运行时,在它之前执行别的程序(六)

公众号&#xff1a;Android系统攻城狮 简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&am…

OpenGLES 实验室之2D篇 第三弹 の 直播白板

本文字数&#xff1a;4555字 预计阅读时间&#xff1a;12分钟 笔者之前发表的音视频文章&#xff0c;有图像的处理&#xff0c;音频的重采样等等&#xff0c;都属于入门级别。通过阅读它们&#xff0c;读者能对音视频有了了解。可在 Gitee 上面回顾。 2023 年&#xff0c;笔者将…

如何使用depcheck检查vue和react的依赖,以后不用把时间浪费在依赖问题上了

当我们在开发 JavaScript 项目时&#xff0c;会引入各种依赖库。但是有些依赖库可能只用到了部分功能&#xff0c;或者已经不再需要了&#xff0c;但是却一直被保留在项目中。 这些未使用的依赖库会占据项目的空间&#xff0c;增加项目的复杂度&#xff0c;影响项目的性能。为…

Elasticsearch --- 索引库、文档操作

一、索引库操作 索引库就类似数据库表&#xff0c;mapping映射就类似表的结构。 我们要向es中存储数据&#xff0c;必须先创建“库”和“表”。 1.1、mapping映射属性 mapping是对索引库中文档的约束&#xff0c;常见的mapping属性包括&#xff1a; type&#xff1a;字段数据…

【python】scikit-learn包:机器学习

环境配置&#xff1a;Scikit-learn包 只支持python语言 安装 WinR &#xff0c;输入指令&#xff1a;pip install -U scikit-learn 数据预处理 数据导入 借助pandas和numpy 进行数据导入与处理 字符串类label的数字化编码 机器学习的函数大部分只能对数字信息进行处理&…

轻量级任务看板做任务管理

利用看板管理工作和任务&#xff0c;可以让团队更高效&#xff0c;也可以一目了然的了解任务进度及问题 1、首先创建一个任务看板 使用看板工具轻量级项目模板创建一个任务看板。 任务看板内包含&#xff1a;列表和任务卡片&#xff0c;列表一般代表任务流程及状态&#xff…

(四)ArcMap基础——要素的选择

要素的选择 当要在已有的数据中选择部分要素时&#xff0c;ArcMap提供了三种方式&#xff1a;按属性选择、按位置选择及按图形选择。 目录 要素的选择一、按属性选择二、按位置选择三、按图形选择 一、按属性选择 通过设置 SQL 查询表达式&#xff0c;用来选择与选择条件匹配…

怎样解决高并发下的I/O瓶颈?

大家好&#xff0c;我是易安。 说起Java I/O&#xff0c;相信你一定不陌生。你可能使用I/O操作读写文件&#xff0c;也可能使用它实现Socket的信息传输…这些都是我们在系统中最常遇到的和I/O有关的操作。 我们都知道&#xff0c;I/O的速度要比内存速度慢&#xff0c;尤其是在现…

【致敬未来的攻城狮计划】— 连续打卡第十五天:FSP固件库外部中断处理编程(外部中断检测按键控制LED闪烁)

系列文章目录 1.连续打卡第一天&#xff1a;提前对CPK_RA2E1是瑞萨RA系列开发板的初体验&#xff0c;了解一下 2.开发环境的选择和调试&#xff08;从零开始&#xff0c;加油&#xff09; 3.欲速则不达&#xff0c;今天是对RA2E1 基础知识的补充学习。 4.e2 studio 使用教程 5.…

J - 在赌场玩

第一周任务 - Virtual Judge (vjudge.net) http://t.csdn.cn/rcwO7 第一周任务 - Virtual Judge (vjudge.net) 【题目描述】 然后所有玩家成对玩&#xff0c;每对玩家只玩一次。因此&#xff0c;例如&#xff0c;如果总共有四个玩家&#xff0c;则进行六场比赛&#xff1a;第…

真题详解(二分查找平均值)-软件设计(六十)

真题详解&#xff08;数据流图平衡&#xff09;-软件设计&#xff08;五十九)https://blog.csdn.net/ke1ying/article/details/130394959 全码&#xff1a;指关系模式所有属性都是这个关系模式的候选码。 RISC特点&#xff1a; 指令种类&#xff1a;少&#xff0c;精简 指令…