服务端实时推送技术之SSE(Server-Send Events)
文章目录
- 前言
- 一、解决方案:
- 1、传统实时处理方案:
- 2、HTML5 标准引入的实时处理方案:
- 3、第三方推送:
- 二、SSE
- 1.引入库
- 1、客户端:
- 2.服务端:
- 三、业务实践:
- 能否做到精准投递?
- 总结
前言
服务端推送,也称为消息推送或通知推送,是一种允许应用服务器主动将信息发送到客户端的能力,为客户端提供了实时的信息更新和通知,增强了用户体验。
服务端推送的背景与需求主要基于以下几个诉求:
-
实时通知:在很多情况下,用户期望实时接收到应用的通知,如新消息提醒、商品活动提醒等。
-
节省资源:如果没有服务端推送,客户端需要通过轮询的方式来获取新信息,会造成客户端、服务端的资源损耗。通过服务端推送,客户端只需要在收到通知时做出响应,大大减少了资源的消耗。
-
增强用户体验:通过服务端推送,应用可以针对特定用户或用户群发送有针对性的内容,如优惠活动、个性化推荐等。这有助于提高用户对应用的满意度和黏性。
常见推送场景有:微信消息通知栏、新闻推送、外卖状态 等等,我们自身的推送场景有:下载、连线请求、直播提醒 …
一、解决方案:
1、传统实时处理方案:
轮询:这是一种较为传统的方式,客户端会定时地向服务端发送请求,询问是否有新数据。服务端只需要检查数据状态,然后将结果返回给客户端。轮询的优点是实现简单,兼容性好;缺点是可能产生较大的延迟,且对服务端资源消耗较高。
长轮询(Long Polling):轮询的改进版。客户端向服务器发送请求,服务器收到请求后,如果有新的数据,立即返回给客户端;如果没有新数据,服务器会等待一定时间(比如30秒超时时间),在这段时间内,如果有新数据,就返回给客户端,否则返回空数据。客户端处理完服务器返回的响应后,再次发起新的请求,如此反复。长轮询相较于传统的轮询方式减少了请求次数,但仍然存在一定的延迟。
2、HTML5 标准引入的实时处理方案:
WebSocket:一种双向通信协议,同时支持服务端和客户端之间的实时交互。WebSocket 是基于 TCP 的长连接,和HTTP 协议相比,它能实现轻量级的、低延迟的数据传输,非常适合实时通信场景,主要用于交互性强的双向通信。
SSE:SSE(Server-Sent Events)是一种基于 HTTP 协议的推送技术。服务端可以使用 SSE 来向客户端推送数据,但客户端不能通过SSE向服务端发送数据。相较于 WebSocket,SSE 更简单、更轻量级,但只能实现单向通信。
两者的主要区别:
SSE | WebSocket | |
---|---|---|
通信 | 单向通信 | 双向通信 |
协议 | HTTP | WebSocket |
自动重连 | 支持 | 不支持,需要客户端自行支持 |
数据格式 | 文本格式,如果需要二进制数据,需要自行编码 | 默认二进制数据,支持文本格式 |
浏览器支持 | 大部分支持,早期Edge浏览器,Internet Explorer不支持 | 主流浏览器(包括移动端)的支持较好 |
3、第三方推送:
常见的有操作系统提供相应的推送服务,如苹果的APNs(Apple Push Notification service)、谷歌的FCM(Firebase Cloud Messaging)等。同时,也有一些跨平台的推送服务,如个推、极光推送、友盟推送等,帮助开发者在不同平台上实现统一的推送功能。
这种推送方式在生活中十分常见,一般你打开手机就能看到各种信息推送,基本就是利用第三方推送来实现。
二、SSE
1.引入库
接下来我们重点讲讲 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>
自动重连:一旦连接断开,浏览器会自动尝试重新建立连接。当然,每个浏览器都有自己的重连策略和措施。因此,重连时间和尝试次数可能因浏览器而异。
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 的支持。这两种方法根据具体需求和场景,可以灵活选择。
三、业务实践:
我们以「文件下载」功能进行说明,一般情况下,大文件的下载,服务端压力比较大、处理时间也比较长,为了有更好的交互体验,我们可以使用异步处理,服务端处理完了之后主动通知 客户端,效果如下:
1)SSE 连接:
先建立 SSE 连接,确保服务端有主动推送消息的能力。
2)异步下载:
长耗时下载任务我们通过异步的方式处理,避免用户在下载页面长时间等待。
3)广播并推送:
下载完成后,我们需要将完成事件推送给客户端。需要注意的是,由于服务是集群部署、SSE 连接在节点本地 Map 维护,这就有可能导致当前客户端的 SSE连接所在节点 与 事件推送节点 是两个独立的节点。
因此,我们这里借助于 Redis 的发布/订阅能力,将消息广播出去,能匹配连接的节点负责将消息推送至客户端、其他节点直接丢弃即可。效果图如下:
能否做到精准投递?
答案也是可以的,我们可以这样来做:
借助 Redis 做中心存储,存储Map<用户,节点IP> 这样的映射关系。
在推送消息之前,先通过映射关系找到该用户的SSE连接所在节点
然后在通过 RPC调用 直接将消息投递到对应的服务节点,最后由该节点进行事件推送。
一般情况下,我们可以用「广播」这种简单粗暴的方式应对大部分场景,毕竟「精准投递」需要中心化的维护节点关系、应对节点变更等,处理起来稍显麻烦。具体视业务场景来做选择即可。
总结
SSE 技术是一种轻量级的实时推送技术,具有支持跨域、使用简单、支持自动重连等特点,使得其在实时消息推送、股票交易等场景下广泛使用。
另外,SSE 相对于 WebSocket 更加轻量级,如果需求场景不需要交互式动作,那么 SSE 是一个不错的选择。
相关文章:

服务端实时推送技术之SSE(Server-Send Events)
文章目录 前言一、解决方案:1、传统实时处理方案:2、HTML5 标准引入的实时处理方案:3、第三方推送: 二、SSE1.引入库1、客户端: 2.服务端:三、业务实践:能否做到精准投递? 总结 前言…...

使用IntelliJ IDEA查看接口的全部实现方法
在大型Java项目中,经常会使用接口和抽象类进行代码设计。为了更好地了解代码结构和功能,我们需要快速查看一个接口的所有实现类。IntelliJ IDEA提供了一些方便的方法来实现这一目标。 1. 点击查看接口的实现子类 在IDEA中,你可以轻松地查看…...

阿里云幻兽帕鲁服务器操作系统类型怎么选择?
使用阿里云服务器搭建幻兽帕鲁操作系统类型选Windows还是Linux?如果对Linux熟悉就选择Linux,相对于windows,Linux更少占用系统资源;如果对Linux不熟悉,首选Windows。事实上,阿里云提供的幻兽帕鲁服务器通过…...

Codeforces Round 927 (Div. 3) LR-remainders的题解
原题描述: C.LR-remains 每次测试时限:2 秒 每次测试的内存限制:256 兆字节 输入:标准输入 输出:标准输出 样例1输入: 4 4 6 3 1 4 2 LRRL 5 1 1 1 1 1 1 LLLLL 6 8 1 2 3 4 5 6 RLLLRR 1 10000 1000…...

HarmonyOS—@Observed装饰器和@ObjectLink嵌套类对象属性变化
Observed装饰器和ObjectLink装饰器:嵌套类对象属性变化 概述 ObjectLink和Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步: 被Observed装饰的类,可以被观察到属性的变化;子组件中ObjectLink装饰器装饰的状…...

The method toList() is undefined for the type Stream
The method toList() is undefined for the type Stream (JDK16) default List<T> toList() { return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray()))); }...

vue+element (el-progress)标签 隐藏百分比(%) ,反向显示 ,自定义颜色, demo 复制粘贴拿去用
1 效果: 2 页面代码: <el-row :gutter"10" ><el-col :span"12"><el-card ><div class"fourqu"><div><span slot"title">{{推送任务TOP5}}</span></div></div><div class&…...

Android轻量级进程间通信Messenger源码分析
一. 概述 Android中比较有代表性的两大通信机制:1. 线程间Handler通信 2. 进程间Binder通信,本篇文章中我们在理解AIDL原理的基础上来解读一下Messenger的源代码, 并结合示例Demo加深理解。 在看本篇文章前,建议先查阅一下笔者的…...

C#开发AGV地图编辑软件
C#自己开发AGV地图编辑软件: 1、自由添加和删除站点、停车位、小车、运行路径。 2、编辑得地图以XML文件保存。 3、导入编辑好地图的XML文件。 4、程序都是源码,可以直接在此基础上进行二次开发。 下载链接:https://download.csdn.net/d…...

嵌入式学习day22 Linux
文件IO: 1. lseek off_t lseek(int fd, off_t offset, int whence); 功能: 重新设定文件描述符的偏移量 参数: fd:文件描述符 offset:偏移量 whence: SEEK_SET 文件开头 …...

不确定性问题的论文笔记
Statistics starting from 01/2024, 仅列出了优秀工作中的一部分 每一年的排列顺序: CVPR, ICLR, ECCV, ICCV, ICML, AAAI, TPAMI,TIP,Arxiv 等 每周更新 2024 论文信息速览笔记是 否 已精读精读笔记Shao W, Xu Y, Peng L, et al. Failure Detection fo…...

C语言推荐书籍
本书详细讲解了C语言的基本概念和编程技巧。全书共17章。第1章、第2章介绍了C语言编程的预备知识。第3章~第15章详细讲解了C语言的相关知识,包括数据类型、格式化输入/输出、运算符、表达式、语句、循环、字符输入和输出、函数、数组和指针、字符和字符串…...

基于uniapp微信小程序的汽车租赁预约系统
随着现代汽车租赁管理的快速发展,可以说汽车租赁管理已经逐渐成为现代汽车租赁管理过程中最为重要的部分之一。但是一直以来我国传统的汽车租赁管理并没有建立一套完善的行之有效的汽车租赁管理系统,传统的汽车租赁管理已经无法适应高速发展,…...

ClickHouse 基础(一)
官网 ClickHouse release 24.1, 2024-01-30 以毫秒为单位查询数十亿行 ClickHouse是用于实时应用和分析的最快、资源效率最高的开源数据库。 安装ClickHouse 使用ClickHouse,你有三个选择: ClickHouse云:官方ClickHouse作为一项服务,-由ClickHouse的创…...

07-k8s中secret资源02-玩转secret
一、回顾secret资源的简单实用 第一步:将想要的数据信息【key:value】中的value值,使用base64编码后,写入secret资源清单中; 第二步:创建secret资源; 第三步:pod资源引用secret资源&…...

HTTP特性
大家好我是苏麟 , 今天说说HTTP特性. 资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 到目前为止,HTTP 常见到版本有 HTTP/1.1,HTTP/2.0,HTTP/3.0,不同版本的 HTTP 特性是不一样的。 这里先用 HTTP/1.1 版本给大家介…...

ARM 之十六 详解 CMSIS 版本变迁、各组件使用示例
目前,CMSIS 已经发展到了第六版,其目录结构也发生了重大的变化。在不断发展中,很多原来 CMSIS 的组件被不断独立出去,并因此成立了很多开源社区,今天就来学习一下! 由于 CMSIS 已经包含了相当丰富的文档,因此,本文重点学习版本之间的变化以及一些实际使用示例。 什么是…...

【北京游戏业:出海竞争实力全面】
本文将深入分析北京的游戏行业发展。在上海、广州、北京、深圳、成都、杭州、福建七大游戏产业中心城市中,北京无疑是出海竞争力最强的游戏产业集群。本文将全面剖析北京游戏行业的发展现状。 北京是中国游戏产业的发源地。拥有从游戏引擎到美术设计等完整的产业链…...

课程大纲:图像处理中的矩阵计算
课程名称:《图像处理中的矩阵计算》 课程简介: 图像处理中的矩阵计算是图像分析与处理的核心部分。本课程旨在教授学员如何应用线性代数中的矩阵计算,以实现各种图像处理技术。我们将通过强调实际应用和实践活动来确保学员能够理解和掌握这些…...

【Go语言】Go语言的数据类型
GO 语言的数据类型 Go 语言内置对以下这些基本数据类型的支持: 布尔类型:bool 整型:int8、byte、int16、int、uint、uintptr 等 浮点类型:float32、float64 复数类型:complex64、complex128 字符串:st…...

2024年2月19日 - mis
当需要在两个或多个子模式间插入文本时,这个特性尤其有用。这里有个脚本,它使用子模式在大数字中插入逗号。 $ echo "1234567" | sed { > :start > s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/ > t start > } 1,234,567 $ 这个脚本将匹…...

【JavaWeb】网上蛋糕商城-项目搭建
学习目标 了解网上蛋糕商城的项目需求 了解网上蛋糕商城的功能结构 熟悉E-R图和数据表的设计 熟悉项目环境的搭建 通过前面章节的学习,相信读者应该已经掌握了Web开发的基础知识,学习这些基础知识就是为开发Web网站奠定基础。如今,电子商…...

【Flink状态管理五】Checkpoint的设计与实现
文章目录 1. Checkpoint的整体设计2. Checkpoint创建源码解析2.1. DefaultExecutionGraphBuilder.buildGraph2.2. ExecutionGraph.enableCheckpointing 由于系统原因导致Flink作业无法正常运行的情况非常多,且很多时候都是无法避免的。对于Flink集群来讲,…...

How to install a specific version of a package in R
How to install a specific version of a package in R 在使用R语言完成数据分析的过程中,很多时候,因为项目实际需要,我们应该指定某些库文件的安装包的版本,这个时候,我们可以基于devtools包中的函数install_version…...

SIGSEGV 段错误
SIGSEGV是在Unix/Linux系统中表示"Segmentation Fault"(分段错误)的信号。当一个进程访问未分配给它的内存、访问超出其内存边界或者访问不允许的内存区域时,就会产生SIGSEGV信号,导致进程被操作系统终止。 SIGSEGV的常…...

OpenCV 4基础篇| OpenCV简介
目录 1. 什么是OpenCV2. OpenCV的发展历程3. 为什么用OpenCV4. OpenCV应用领域5. OpenCV的功能模块5.1 基本模块5.2 扩展模块5.3 常用函数目录 1. 什么是OpenCV OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它…...

Vue常用内置指令,代码Demo演示和讲解
文章目录 一、v-text二、v-html三、v-bind四、v-model五、v-on六、v-show七、v-if、v-else八、v-for九、v-cloak 一、v-text 作用,更改标签的显示值使用 v-text 会读取 data中的变量值如果变量存在替换原来的值,如果不存在则报错 <!DOCTYPE html>…...

Spring设计模式之工厂模式创建Bean对象
BeanFactory和Application是Spring容器中创建和管理Bean对象的接口,但是它们的实现方式不同。 BeanFactory: BeanFactory采用延迟初始化策略,只有应用程序向容器请求特定的Bean时才创建该Bean对象。它的启动速度很快,但在程序运…...

Elasticsearch 别名(Aliases)的作用
Elasticsearch 8.4.3 别名(Aliases) 一. 介绍二. 别名的优势三. 别名的基本操作3.1 创建别名3.2 查询别名关联的索引3.3 删除别名3.4 更新别名3.5 通过别名查询数据 前言 这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接…...

基于vue的个性化推荐餐饮系统Springboot
项目:基于vue的个性化推荐餐饮系统Springboot 摘要 现代信息化社会下的数据管理对活动的重要性越来越为明显,人们出门可以通过网络进行交流、信息咨询、查询等操作。网络化生活对人们通过网上购物也有了非常大的考验,通过网上进行点餐的人也…...