轮询解决方案

news2024/11/15 13:34:04

概述

轮询的使用场景:

  • 股票 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.coloradmin.cn/o/2130519.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

18 C语言实现深度优先搜索

#include "stdio.h" #include "stdlib.h" #include "stdbool.h"#define MaxVertex 10typedef char ElemType;typedef struct Node { //链表中的值int nextVertex;//指向的位置struct Node *next; } Node;struct HeadNode {//链表头ElemType data…

第309题|证明函数单调有界的核心思路 |武忠祥老师每日一题

解题思路&#xff1a;两个极限存在准则&#xff1a;1.夹闭。 2.单调有界。 这里题目告诉了我们f(x)的导数,如果我们判断出了 导数的正负&#xff0c;就能得出f&#xff08;x&#xff09;的单调性。 显然是大于0的&#xff0c;看后半部分:是否大于0&#xff0c;这里直接比较和…

借助大模型将文档转换为视频

利用传统手段将文档内容转换为视频&#xff0c;比如根据文档内容录制一个视频&#xff0c;不仅需要投入大量的时间和精力&#xff0c;而且往往需要具备专业的视频编辑技能。使用大模型技术可以更加有效且智能化地解决上述问题。本实践方案旨在依托大语言模型&#xff08;Large …

[001-03-007].第26节:分布式锁迭代1->基于setnx命令实现分布式锁:

我的博客大纲 我的后端学习大纲 1、setnx命令&#xff1a; 2、逻辑梳理&#xff1a; 1.借助于redis中的命令setnx(key, value)&#xff0c;key不存在就新增&#xff0c;存在就什么都不做。同时有多个客户端发送setnx命令&#xff0c;只有一个客户端可以成功&#xff0c;返回1&…

velero v1.14.1迁移kubernetes集群

1 概述 velero是vmware开源的一个备份和恢复工具&#xff0c;可作用于kubernetes集群下的任意对象和应用数据&#xff08;PV上的数据&#xff09;。github地址是https://github.com/vmware-tanzu/velero。 对于应用数据&#xff0c;可分文件级别的复制和块级别的复制。文件级…

大模型国产化算力方案

方案1 - 摩尔线程 MTT S4000 | 摩尔线程 大模型训练平台架构 摩尔线程大模型训练平台&#xff0c;完全兼容 CUDA 和 Pytorch 训练系统&#xff0c;支持 Megatron-LM、DeepSpeed、FSDP 和 Colossal-AI 等大模型分布式训练框架。具有全兼容、高性能、高灵活性和简单易用等特点…

【算法系列】双指针:283.移动零

目录 双指针介绍 283.移动零 1.题目介绍 2. 解决思路&#xff1a; 动图演示 代码&#xff1a; 双指针介绍 常⻅的双指针有两种形式&#xff0c;⼀种是对撞指针&#xff0c;⼀种是快慢指针。 对撞指针&#xff1a;一般用于顺序结构中&#xff0c;也称左右指针。 1、对撞指…

dp+观察,CF 1864 D. Matrix Cascade

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 https://codeforces.com/problemset/problem/1864/D 二、解题报告 1、思路…

在虚拟机安装mysql数据库

一、安装步骤&#xff08;下载包-传输软件包-安装包-启用仓库-使用yum安装服务器&#xff09; 1、要在mysql官网下载yum仓库包 2、下载好rpm包后&#xff0c;将其通过xftp传输到root目录下 3、使用sudo yum install yum的仓库名&#xff08;sudo yum install mysql-community-…

当人工智能聊天机器人出现问题时

在快速发展的人工智能领域&#xff0c;出现了一项新的挑战。“人工智能私语者”正在通过说服行为良好的聊天机器人打破自己制定的规则来探索人工智能伦理的界限。 这些漏洞被称为即时注入或“越狱”&#xff0c;它们暴露了人工智能系统的漏洞&#xff0c;引发了人们对其安全性…

如何使用python中的math

首先&#xff0c;导入math函数库。 一、计算平方根 输入一个数值&#xff0c;保存在变量n中。 用函数sqrt&#xff0c;计算变量平方根的值。 二、计算幂 可以用函数exp&#xff0c;计算e的x次幂。 三、计算对数 设置两个数&#xff0c;保存在变量n和a中。 接着&#xff0c;用…

Xcode报错:No exact matches in reference to static method ‘buildExpression‘

Xcode报错1&#xff1a;No exact matches in reference to static method buildExpression Xcode报错2&#xff1a;Type () cannot conform to View 这两个报错都是因为在SwiftUI的View的Body里面使用了ForEach循环,却没有在ForEach循环闭包的内部返回视图&#xff0c;而是做了…

云服务器Jenkins部署Springboot项目以及Vue项目

一、创建Springboot项目并且上传git 在gitee上创建一个仓库 点击初始化仓库 在idea克隆gitee仓库 创建一个普通的Springboot项目只需要勾选springweb 上传创建的新模块到gitee仓库 二、安装Maven maven的官网地址&#xff1a;https://maven.apache.org/download.cgi 点击下载…

OJ 括号生成

题目&#xff1a; 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例&#xff1a; 代码分析&#xff1a; class Solution { public://进行回溯调用vector<string> generateParenthesis(int n) {if(…

形式向好、成本较低、可拓展性较高的名厨亮灶开源了

简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;在强大视频算法加…

你的 Java 对象占用了多少内存

在本文中&#xff0c;我们将讨论 JVM 如何在内存中存储对象&#xff1a;它们的对齐方式。 对象表示是理解 JVM 底层机制的重要主题&#xff0c;它提供了有助于应用程序调优的见解。 这里&#xff0c;我们主要关注填充和对齐&#xff0c;而不是 JVM 如何在内存中表示对象。要获…

CSS 图片廊:打造精美视觉体验

CSS 图片廊:打造精美视觉体验 随着互联网技术的发展,网页设计越来越注重用户体验和视觉效果的呈现。CSS(层叠样式表)作为网页设计的重要工具,能够帮助开发者创建出既美观又实用的图片展示效果。本文将详细介绍如何使用CSS来打造一个精美的图片廊,以提升网页的视觉效果和…

【C++二分查找】2517. 礼盒的最大甜蜜度

本文涉及的基础知识点 C二分查找 贪心&#xff08;决策包容性) LeetCode 2517. 礼盒的最大甜蜜度 给你一个正整数数组 price &#xff0c;其中 price[i] 表示第 i 类糖果的价格&#xff0c;另给你一个正整数 k 。 商店组合 k 类 不同 糖果打包成礼盒出售。礼盒的 甜蜜度 是礼…

RabbitMQ高级篇,进阶内容

强烈建议在看本篇博客之前快速浏览文章&#xff1a;RabbitMQ基础有这一篇就够了 RabbitMQ高级篇 0. 前言1. 发送者的可靠性1.1 生产者重试机制1.2 生产者确认机制1.3 实现生产者确认 2. MQ的可靠性2.1 MQ持久化2.2 LazyQueue 3. 消费者的可靠性3.1 消费者确认机制3.2 失败重试策…

25、Wpf之App资源应用

开发平台&#xff1a;Win10 64位 开发环境&#xff1a;VS2022(64位) Preview .NET Framework&#xff1a;.NET 6 文章目录 一 Resources1.1 Application中定义资源1.2 样式&#xff08;Styles&#xff09;1.3 模板&#xff08;Templates&#xff09;1.4 数据转换器&#xff08;…