当前位置: 首页 > article >正文

轮询解决方案

概述

轮询的使用场景:

  • 股票 K 线图
  • 聊天
  • 重要通知,实时预警

这些场景都是都要实时性的。

http 是请求响应模式,一定需要先请求,后响应。

解决方案:

  • 短轮询:interval 定时发送请求。问题:大量无意义的请求(老舔狗了😭),频繁打开关闭连接,而且定时很难确定固定的时间。
  • 长轮询:发送请求,直接有响应后,才发送下一次请求。问题:客户端长时间没有响应,导致超时,断开 TCP 连接(解决方法是当超时了立即再发送一次请求 - 抛出异常再发请求);而且服务器没有响应的时候挂起请求也需要占用服务器资源。
  • Websocket:WebSocket也是建立在TCP协议之上的,利用的是TCP全双工通信的能力,使用WebSocket,会经历两个阶段:握手阶段、通信阶段。缺点也有:维持 tcp 连接需要耗费资源。

示例

一、WebSocket + 轮询 回退机制

1. 后端(Node.js 使用 ws 库实现 WebSocket)

安装依赖:

npm install express ws

创建 server.js

const express = require('express');
const WebSocket = require('ws');

const app = express();
const port = 3000;

// 使用 HTTP 服务作为静态文件服务器
app.use(express.static('public'));

// 轮询 API 模拟
app.get('/api/data', (req, res) => {
  const data = { timestamp: Date.now(), message: 'Polling data' };
  res.json(data);
});

// 创建 WebSocket 服务
const server = app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});

const wss = new WebSocket.Server({ server });

// WebSocket 连接处理
wss.on('connection', (ws) => {
  console.log('WebSocket client connected');

  // 模拟定时推送数据
  const intervalId = setInterval(() => {
    const data = { timestamp: Date.now(), message: 'WebSocket data' };
    ws.send(JSON.stringify(data));
  }, 2000);
  
  //10s 后断开连接
  setTimeout(() => {
      ws.close();
  }, 10000);

  ws.on('close', () => {
    console.log('WebSocket client disconnected');
    clearInterval(intervalId);
  });
});
2. 前端代码

创建 public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebSocket + Polling</title>
  <script src="app.js" defer></script>
</head>
<body>
  <h1>WebSocket + Polling 回退机制</h1>
  <div id="output"></div>
</body>
</html>

创建 public/app.js

let websocket;
let pollingInterval = 5000; // 初始轮询间隔为5秒
let pollingTimeout;

const outputDiv = document.getElementById('output');

// 显示数据
function displayData(data) {
  const dataElement = document.createElement('p');
  dataElement.textContent = `时间戳: ${data.timestamp}, 消息: ${data.message}`;
  outputDiv.appendChild(dataElement);
}

// 初始化 WebSocket 连接
function connectWebSocket() {
  websocket = new WebSocket('ws://localhost:3000');

  websocket.onopen = function() {
    console.log('WebSocket 连接成功');
    clearTimeout(pollingTimeout); // WebSocket 成功连接后停止轮询
  };

  websocket.onmessage = function(event) {
    const data = JSON.parse(event.data);
    console.log('接收到 WebSocket 数据:', data);
    displayData(data);
  };

  websocket.onclose = function() {
    console.log('WebSocket 连接断开,启动轮询');
    startPolling();
  };

  websocket.onerror = function(error) {
    console.error('WebSocket 错误:', error);
    websocket.close();
  };
}

// 轮询函数
function startPolling() {
  fetch('/api/data')
    .then(response => response.json())
    .then(data => {
      console.log('轮询获取的数据:', data);
      displayData(data);
    })
    .catch(error => {
      console.error('轮询错误:', error);
    })
    .finally(() => {
      // 动态调整轮询频率(可选择)
      pollingTimeout = setTimeout(startPolling, pollingInterval);
      pollingInterval = Math.min(pollingInterval * 2, 60000); // 最长间隔不超过60秒
    });
}

// 页面加载时尝试建立 WebSocket 连接
window.onload = function() {
  connectWebSocket();
};
启动步骤:
  1. 在终端运行 node server.js
  2. 访问 http://localhost:3000/,页面会首先尝试通过 WebSocket 获取数据,10s 之后, WebSocket 连接断开则自动回退到轮询方式获取数据,然后可以看到不断向后端接口发送请求进行轮训。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


二、SSE + 轮询 回退机制

1. 后端(Node.js 使用原生 EventSource 实现 SSE)

安装依赖:

npm install express

创建 server.js

const express = require('express');
const app = express();
const port = 3000;

// 使用 HTTP 服务作为静态文件服务器
app.use(express.static('public'));

// 轮询 API 模拟
app.get('/api/data', (req, res) => {
    const data = { timestamp: Date.now(), message: 'Polling data' };
    res.json(data);
});

// SSE 接口
app.get('/api/sse', (req, res) => {
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    // 解决跨域
    res.setHeader('Access-Control-Allow-Origin', '*');

    const sendSSE = () => {
        const data = { timestamp: Date.now(), message: 'SSE data' };
        res.write(`data: ${JSON.stringify(data)}`);
    };

    const intervalId = setInterval(sendSSE, 2000);

    // 10s 后停止发送数据 关闭SSE连接
    setTimeout(() => {
        clearInterval(intervalId);
        res.end();
    }, 10000);

    // 当客户端断开连接时清除定时器
    req.on('close', () => {
        clearInterval(intervalId);
    });
});

app.listen(port, () => {
    console.log(`Server listening on port ${port}`);
});
2. 前端代码

创建 public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SSE + Polling</title>
  <script src="app.js" defer></script>
</head>
<body>
  <h1>SSE + Polling 回退机制</h1>
  <div id="output"></div>
</body>
</html>

创建 public/app.js

let eventSource;
let pollingInterval = 5000; // 初始轮询间隔为5秒
let pollingTimeout;

const outputDiv = document.getElementById('output');

// 显示数据
function displayData(data) {
    const dataElement = document.createElement('p');
    dataElement.textContent = `时间戳: ${data.timestamp}, 消息: ${data.message}`;
    outputDiv.appendChild(dataElement);
}

// 初始化 SSE 连接
function connectSSE() {
    eventSource = new EventSource('http://localhost:3000/api/sse');

    eventSource.onopen = function() {
        console.log('SSE 连接成功');
        clearTimeout(pollingTimeout); // SSE 成功连接后停止轮询
    };

    eventSource.onmessage = function(event) {
        const data = JSON.parse(event.data);
        console.log('接收到 SSE 数据:', data);
        displayData(data);
    };

    eventSource.onerror = function(error) {
        console.error('SSE 错误:', error);
        eventSource.close();
        startPolling(); // SSE 断开后启动轮询
    };
}

// 轮询函数
function startPolling() {
    fetch('/api/data')
        .then(response => response.json())
        .then(data => {
            console.log('轮询获取的数据:', data);
            displayData(data);
        })
        .catch(error => {
            console.error('轮询错误:', error);
        })
        .finally(() => {
            // 动态调整轮询频率
            pollingTimeout = setTimeout(startPolling, pollingInterval);
            pollingInterval = Math.min(pollingInterval * 2, 60000); // 最长间隔不超过60秒
        });
}

// 页面加载时尝试建立 SSE 连接
window.onload = function() {
    connectSSE();
};
启动步骤:
  1. 在终端运行 node server.js
  2. 访问 http://localhost:3000/,页面会首先尝试通过 SSE 获取数据,若 SSE 连接断开则自动回退到轮询方式获取数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

贴一篇写的不错的文章:技术方案实践: 前端轮询方案实现 & 思考_前端轮询方式的实现-CSDN博客


http://www.kler.cn/a/302018.html

相关文章:

  • 图像处理技术椒盐噪声
  • 时钟之CSS+JS版
  • LeetCode654.最大二叉树
  • STM32芯片EXIT外部中断的配置与原理
  • C++静态成员
  • 玩转ChatGPT:文献阅读 v2.0
  • C++20 新特征:概念(Concepts)全面解析
  • PointNet++改进策略 :模块改进 | SPVConv, 体素和点云特征融合提升小目标检测能力
  • html+css+js网页设计 旅游 龙门石窟4个页面
  • Spring Boot 注解探秘:Bean 管理的艺术
  • 【Qt应用】Qt编写简易登录注册界面
  • DAY14信息打点-JS 架构框架识别泄漏提取API 接口枚举FUZZ 爬虫插件项目
  • echarts实现湖南省地图并且定时轮询
  • jsp+servlet+mysql机票订票管理系统
  • Excel排序错误原因之一
  • 打造高效实时数仓,从Hive到OceanBase的经验分享
  • Tensorboard 基础与使用-——界面介绍
  • 使用FastJson2将对象转成JSON字符串时,小数位“0”开头时转换出错
  • 深入理解Java虚拟机:Jvm总结-Java内存区域与内存溢出异常
  • [数据集][目标检测]烟叶病害检测数据集VOC+YOLO格式612张3类别
  • Cmake之3.22版本重要特性及用法实例(十九)
  • 小众创新组合!LightGBM+BO-Transformer-LSTM多变量回归交通流量预测(Matlab)
  • 计算两个数据集之间的皮尔森相关系数与其p值 scipy.stats.pearsonr()
  • 解决 Ubuntu 20.04 上 Fail2Ban 启动失败问题:指定 systemd 后端
  • nnunet报错 the direction does not match between the images
  • STM32-HAL库开发快速入门