Long Polling 对比 WebSocket 在 Rust 中:实时性的较量

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么 Rust 是实时应用的理想选择?

\n\n

想象一下这个场景:您正在用 Rust 编写一个聊天服务器、股票交易终端或监控系统。用户希望立即收到更新。没有人喜欢手动点击“刷新”按钮——那已经是上个世纪的事了。但是,如何实现从服务器到客户端的数据推送呢?

\n\n

很长一段时间里,唯一的方法是 long polling——一个优雅但有点“取巧”的技巧。随后 WebSocket 的出现颠覆了 Web 开发的世界。而现在我们有了 Rust——一种能够在不牺牲内存安全的前提下,从这两种方法中榨取最大性能的语言。

\n\n

在本文中,我们不仅会比较 Rust 中的 long polling 和 WebSocket——我们还会编写可运行的示例,启动它们,并深入了解其内部机制。如果您正在用 Rust 编写实时应用,并且在技术选型上犹豫不决,那么您来对地方了。

\n\n

通俗理解 Long Polling

\n\n

Long polling 是一种技术,客户端向服务器发起一个 HTTP 请求,然后……等待。服务器不会立即响应,而是保持连接打开,直到有新事件发生或超时。

\n\n

想象一下,您给朋友打电话说:“包裹到了就给我打电话”——然后您在线等了一个小时。这就是 long polling。普通的 polling 则是您每 5 分钟回拨一次:“喂,到了吗?”

\n\n

使用 Actix-web 在 Rust 中实现 Long Polling

\n\n

让我们用 actix-web 编写一个最简单的服务器,它使用 long polling。我们会有一个消息队列——当新消息出现时,所有“等待者”都会收到它。

\n\n
use actix_web::{web, App, HttpServer, HttpRequest, HttpResponse};\nuse std::sync::Mutex;\nuse tokio::sync::broadcast;\n\nstruct AppState {\n    tx: broadcast::Sender<String>,\n}\n\nasync fn long_poll_handler(data: web::Data<Mutex<AppState>>) -> HttpResponse {\n    let mut rx = {\n        let state = data.lock().unwrap();\n        state.tx.subscribe()\n    };\n\n    // 等待新消息,最多 30 秒\n    match tokio::time::timeout(\n        std::time::Duration::from_secs(30),\n        rx.recv()\n    ).await {\n        Ok(Ok(msg)) => HttpResponse::Ok().body(msg),\n        Ok(Err(_)) => HttpResponse::InternalServerError().finish(),\n        Err(_) => HttpResponse::NoContent().finish(), // 超时\n    }\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    let (tx, _) = broadcast::channel::<String>(100);\n\n    HttpServer::new(move || {\n        App::new()\n            .app_data(web::Data::new(Mutex::new(AppState { tx: tx.clone() })))\n            .route("/poll", web::get().to(long_poll_handler))\n    })\n    .bind("127.0.0.1:8080")?\n    .run()\n    .await\n}
\n\n

客户端只需挂载在 GET /poll 上。当有消息到达时,服务器就会响应。如果 30 秒内没有消息,则返回 204 No Content,客户端会重新发起请求。

\n\n

优点:

\n
    \n
  • 通过普通的 HTTP 工作——无需支持 WebSocket 的代理服务器
  • \n
  • 易于与现有的 REST API 集成
  • \n
  • 非常适合代理会阻止 WebSocket 的企业网络环境
  • \n
\n\n

缺点:

\n
    \n
  • 每个“轮询”都保持一个连接——如果有 10,000 个客户端,就有 10,000 个打开的 TCP 连接
  • \n
  • 延迟:客户端在超时后仍需重新连接
  • \n
  • 服务器需要消耗资源来维持“挂起”的请求
  • \n
\n\n

Rust 中的 WebSocket:当速度成为关键

\n\n

WebSocket 不再是“打电话等待”,而是一个全双工通道。服务器和客户端可以随时交换消息,没有 HTTP 头部的开销。

\n\n

在 Rust 生态系统中,WebSocket 资源丰富:tokio-tungstenite 用于底层操作,actix-web 内置了 WebSocket 支持。

博客

书籍推荐