Rust 实战丨SSE(Server-Sent Events)

2024-06-10 07:44
文章标签 实战 rust server sse events sent

本文主要是介绍Rust 实战丨SSE(Server-Sent Events),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

📌 SSE(Server-Sent Events)是一种允许服务器向客户端浏览器推送信息的技术。它是 HTML5 的一部分,专门用于建立一个单向的从服务器到客户端的通信连接。SSE的使用场景非常广泛,包括实时消息推送、实时通知更新等。

SSE 的本质

严格地说,HTTP 无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。

也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。

SSE 就是利用这种机制,使用流信息向浏览器推送信息。它基于 HTTP 协议,目前除了 IE/Edge,其他浏览器都支持。

特点

  1. 持续连接:与传统的 HTTP 请求不同,SSE 保持连接开放,服务器可以随时发送消息。
  2. 文本数据流:SSE 主要传输文本数据,这些数据以特定的格式流式传输,使得每条消息都是简单的文本格式。
  3. 内置重连机制:浏览器会自动处理连接中断和重连,包括在重连请求中发送最后接收的事件 ID,以便服务器从正确的位置恢复发送事件。
  4. 简单的客户端处理:在浏览器中,使用 JavaScript 的 EventSource 接口处理 SSE 非常简单,只需几行代码即可监听服务器发来的事件。

工作原理

  1. 建立连接:客户端通过创建一个 EventSource 对象请求特定的 URL 来启动 SSE 连接。这个请求是一个标准的 HTTP 请求,但会要求服务器以特定方式响应。
  2. 服务器响应:服务器响应必须设置 Content-Typetext/event-stream,然后保持连接打开。
  3. 发送消息:服务器可以通过持续发送数据格式为特定事件流的消息来推送更新。每个消息包括一个可选的事件类型、数据和一个可选的 ID。
    • 数据:实际的消息内容,以 data: 开头,多行数据以双换行符 \n\n 结束。
    • 事件类型:允许客户端根据事件类型来监听,以 event: 开头。
    • ID:如果连接中断,客户端将发送包含上次接收的最后一个ID的 Last-Event-ID 头,以便服务器从断点继续发送数据。

实战

客户端

<!DOCTYPE html>
<html>
<head><title>SSE Test</title>
</head>
<body>
<h1>Server-Sent Events Test</h1>
<div id="events"></div>
<script>// 确保这里的URL匹配你的服务器地址和端口var eventSource = new EventSource('http://localhost:8000/events');eventSource.onmessage = function(event) {console.log('New event:', event.data);document.getElementById('events').innerHTML += event.data + '<br>';};
</script>
</body>
</html>

Rust 服务端

Rust 实现演示

依赖:

anyhow = "1.0.86"
axum = { version = "0.7.5" }
chrono = "0.4.38"
futures-core = "0.3.30"
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread", ] }
tokio-stream = "0.1.15"
tower-http = { version = "0.5.2", features = ["cors"] }

代码:

use std::time::Duration;use axum::{response::{sse::Event, Sse},routing::get,Router,
};
use tokio::{net::TcpListener, time::interval};
use tokio_stream::{wrappers::IntervalStream, StreamExt};
use tower_http::cors::{Any, CorsLayer};#[tokio::main]
async fn main() -> anyhow::Result<()> {let cors = CorsLayer::new().allow_headers(Any).allow_origin(Any).allow_headers(Any).allow_credentials(false);let listener = TcpListener::bind("0.0.0.0:8000").await?;let app = Router::new().route("/events", get(sse_handler)).layer(cors);axum::serve(listener, app).await?;Ok(())
}async fn sse_handler() -> Sse<impl futures_core::Stream<Item = Result<Event, axum::Error>>> {let interval = interval(Duration::from_secs(1));let stream = IntervalStream::new(interval).map(|_| {let data = format!("{}\n\n", chrono::Local::now().to_rfc2822());Ok(Event::default().data(data))});Sse::new(stream)
}

Go 服务端

Go 实现演示

package mainimport ("fmt""log""net/http""time"
)func sseHandler(w http.ResponseWriter, r *http.Request) {// 设置头部信息,确保允许跨域,并且告诉浏览器这是一个事件流w.Header().Set("Content-Type", "text/event-stream")w.Header().Set("Cache-Control", "no-cache")w.Header().Set("Connection", "keep-alive")w.Header().Set("Access-Control-Allow-Origin", "*")// 不断发送消息for {// 生成服务器时间,并发送给客户端now := time.Now()// 生成消息,格式为 data: {content} \n\nmsg := fmt.Sprintf("data: %s\n\n", now.Format(time.DateTime))// 发送消息if _, err := fmt.Fprintf(w, msg); err != nil {log.Println("write error:", err)break}// 刷新响应缓冲,确保即时发送flusher, ok := w.(http.Flusher)if !ok {log.Println("Streaming unsupported!")break}flusher.Flush()// 每秒发送一次time.Sleep(1 * time.Second)}
}func main() {http.HandleFunc("/events", sseHandler)log.Println("Server started on port 8000...")log.Fatal(http.ListenAndServe(":8000", nil))
}

这篇关于Rust 实战丨SSE(Server-Sent Events)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1047557

相关文章

Window Server创建2台服务器的故障转移群集的图文教程

《WindowServer创建2台服务器的故障转移群集的图文教程》本文主要介绍了在WindowsServer系统上创建一个包含两台成员服务器的故障转移群集,文中通过图文示例介绍的非常详细,对大家的... 目录一、 准备条件二、在ServerB安装故障转移群集三、在ServerC安装故障转移群集,操作与Ser

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

Rust 数据类型详解

《Rust数据类型详解》本文介绍了Rust编程语言中的标量类型和复合类型,标量类型包括整数、浮点数、布尔和字符,而复合类型则包括元组和数组,标量类型用于表示单个值,具有不同的表示和范围,本文介绍的非... 目录一、标量类型(Scalar Types)1. 整数类型(Integer Types)1.1 整数字

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

Golang使用minio替代文件系统的实战教程

《Golang使用minio替代文件系统的实战教程》本文讨论项目开发中直接文件系统的限制或不足,接着介绍Minio对象存储的优势,同时给出Golang的实际示例代码,包括初始化客户端、读取minio对... 目录文件系统 vs Minio文件系统不足:对象存储:miniogolang连接Minio配置Min

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

Rust中的Option枚举快速入门教程

《Rust中的Option枚举快速入门教程》Rust中的Option枚举用于表示可能不存在的值,提供了多种方法来处理这些值,避免了空指针异常,文章介绍了Option的定义、常见方法、使用场景以及注意事... 目录引言Option介绍Option的常见方法Option使用场景场景一:函数返回可能不存在的值场景

SQL Server数据库磁盘满了的解决办法

《SQLServer数据库磁盘满了的解决办法》系统再正常运行,我还在操作中,突然发现接口报错,后续所有接口都报错了,一查日志发现说是数据库磁盘满了,所以本文记录了SQLServer数据库磁盘满了的解... 目录问题解决方法删除数据库日志设置数据库日志大小问题今http://www.chinasem.cn天发

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置