网页接收服务端消息的几种方式

介绍

网页接收服务端消息,一般有如下几种方式

  • 直接轮询
  • WebSocket 长链接
  • 长轮询
  • Server send event

一、短轮询方式

前端页面按照时间间隔轮询拉取最新消息

缺点:

  • 实时性差!最长耗时等于时间间隔耗时
  • 没有消息也会有大量的请求,消耗服务器资源

优点:

  • 开发简单,可以快速实现功能

二、TCP 长链接 WebSocket 和 FlashSocket

WebSocket 协议

WebSocket 是一种全双工通信协议,建立在 TCP 之上,允许客户端(如浏览器)与服务器之间建立持久连接,实现双向实时数据传输。webSocket 解决了 Http 请求-响应模型无法实现服务器主动推送的问题。

特点:双向通信、持久链接、低延迟(数据帧小,无需每次携带头部)、原生支持(现代浏览器均原生支持)

链接:通过 Http 协议的 upgrade 方法建立 tcp 长链接

WebSocket 服务集群如何精确推送

方案 1:集中式连接管理 + 消息路由(推荐)
  • 每个服务节点维护自己当前连接的用户(如内存 Map:userId -> WebSocketSession)
  • 引入一个集中式存储(如 Redis、ETCD、ZooKeeper)记录"用户ID → 所在节点标识"的映射关系。
  • 发送消息时,先查集中存储,找到目标用户所在的节点,再通过节点间通信(如 RPC、消息队列)将消息路由到目标节点,由目标节点通过本地 WebSocket 推送。
方案 2:通过消息队列的消息广播
  • 所有节点订阅相同的 MQ 队列
  • 各个节点需要发送消息,只需要将消息发送到队列中
  • 各个节点消费消息时,只需判断对应的消息接受用户是否连接到本服务了,连接到本服务了则发送消息

FlashSocket 已被淘汰的历史产物

FlashSocket 并不是一个官方协议,而是在 Flash 时代,开发者利用 Adobe Flash 的 Socket API 实现的浏览器端 TCP 长连接方案,用于在 WebSocket 出现之前模拟"实时通信"。

诞生背景:

  • 2005~2012 年,浏览器原生不支持 WebSocket。
  • Flash Player 支持 TCP Socket,可绕过浏览器限制实现"伪长连接"。
  • 需要浏览器安装 Flash 插件 + 用户授权安全策略。

三、Http 长轮询

长轮询是在 WebSocket 普及之前,实现**"伪实时"**通信的一种经典方案。它通过"拉"模式模拟"推"效果:

客户端发起 HTTP 请求 → 服务器"hold 住"请求不立即返回 → 有新消息时才响应 → 客户端收到后立即再次发起新请求

plain 复制代码
客户端                  服务器
   │                       │
   ├─ 请求新消息 ─────────>│
   │                       │
   │                       │ ← 无消息?Hold 住连接(不返回)
   │                       │
   │                       │ ← 有新消息到达!立即返回数据
   │<──────── 响应消息 ────┤
   │                       │
   ├─ 立即再次请求 ────────>│
   │                       │
   │                       │ ← 继续 hold 或返回...

与普通轮询(短轮询)对比

类型 普通轮询 长轮询
请求频率 固定间隔(如每秒1次) 有响应后立即重发
服务器压力 高(频繁空请求) 低(无消息时无响应)
实时性 有延迟(取决于间隔) 较高(消息到达即返回)
资源占用 连接短,但频繁 连接长,但数量可控

优点和缺点:

在现代项目中,优先使用 WebSocket;长轮询仅用于兼容性要求高或临时过渡场景。

** 优点:**

  • 兼容性好:所有浏览器都支持 HTTP。
  • 实现简单:无需特殊协议或插件。
  • 适合低频消息推送(如通知、订单状态变更)。

缺点:

  • 每次请求有 HTTP 头开销。
  • 服务器需维持大量挂起连接(消耗线程/内存)。
  • 不如 WebSocket 实时和高效。
  • 超时、断连需手动处理。

SpringBoot 案例

java 复制代码
// MessageManager.java
@Component
public class MessageManager {

    // 模拟每个用户的"消息阻塞队列"
    private final Map<String, BlockingQueue<String>> userQueues = new ConcurrentHashMap<>();

    // 获取用户队列(不存在则创建)
    private BlockingQueue<String> getQueue(String userId) {
        return userQueues.computeIfAbsent(userId, k -> new LinkedBlockingQueue<>());
    }

    // 发送消息给用户
    public void sendMessage(String userId, String message) {
        BlockingQueue<String> queue = getQueue(userId);
        queue.offer(message); // 非阻塞放入
    }

    // 长轮询:等待消息(最多30秒)
    public String awaitMessage(String userId, long timeoutMillis) throws InterruptedException {
        BlockingQueue<String> queue = getQueue(userId);
        return queue.poll(timeoutMillis, TimeUnit.MILLISECONDS); // 阻塞等待
    }

    // 可选:清理用户队列(用户下线时调用)
    public void removeUser(String userId) {
        userQueues.remove(userId);
    }
}

// LongPollingController.java
@RestController
public class LongPollingController {

    @Autowired
    private MessageManager messageManager;

    /**
     * 长轮询接口:客户端调用,等待新消息
     */
    @GetMapping("/long-polling")
    public ResponseEntity<String> longPolling(@RequestParam String userId) {
        try {
            // 最多等待 30 秒
            String message = messageManager.awaitMessage(userId, 30_000);

            if (message != null) {
                return ResponseEntity.ok(message);
            } else {
                return ResponseEntity.status(204).body(""); // 204 No Content 表示超时无消息
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return ResponseEntity.status(500).body("Interrupted");
        }
    }

    /**
     * 模拟发送消息接口(用于测试)
     */
    @GetMapping("/send")
    public String sendMessage(@RequestParam String to, @RequestParam String msg) {
        messageManager.sendMessage(to, msg);
        return "Message sent to " + to + ": " + msg;
    }
}

基于 Http 的 SSE 技术

七、WebSocket和SSE对比

基于Http的Server-Sent Events 技术

基于Http的Server-Sent Events 技术

相关推荐
onebyte8bits5 小时前
前端国际化(i18n)体系设计与工程化落地
前端·国际化·i18n·工程化
C澒5 小时前
前端分层架构实战:DDD 与 Clean Architecture 在大型业务系统中的落地路径与项目实践
前端·架构·系统架构·前端框架
BestSongC5 小时前
行人摔倒检测系统 - 前端文档(1)
前端·人工智能·目标检测
0思必得06 小时前
[Web自动化] Selenium处理滚动条
前端·爬虫·python·selenium·自动化
Misnice6 小时前
Webpack、Vite、Rsbuild区别
前端·webpack·node.js
青茶3606 小时前
php怎么实现订单接口状态轮询(二)
前端·php·接口
大橙子额7 小时前
【解决报错】Cannot assign to read only property ‘exports‘ of object ‘#<Object>‘
前端·javascript·vue.js
爱喝白开水a8 小时前
前端AI自动化测试:brower-use调研让大模型帮你做网页交互与测试
前端·人工智能·大模型·prompt·交互·agent·rag
董世昌418 小时前
深度解析ES6 Set与Map:相同点、核心差异及实战选型
前端·javascript·es6
青春给了代码9 小时前
基于WebSocket实现在线语音(实时+保存)+文字双向传输完整实现
网络·websocket·网络协议