摘要:本文介绍消息推送的三种常见方式轮询(定时请求,易增负担)与长轮询(阻塞请求至有数据 / 超时,减少请求)、SSE(HTTP 单向实时传输,纯文本、自动重连)、WebSocket(全双工通信,含客户端 API 与 Java 服务端 Endpoint 实现)。

1. 消息推送常见方式

1.1 轮询方式

轮询

  • 工作机制:浏览器按照指定的时间间隔,不断向服务器发送 HTTP 请求(如示例中的 GET/poll)。服务器收到请求后,会实时返回数据给浏览器。
  • 特点不管服务器有没有新数据,浏览器都会定时发请求这种方式可能导致请求过于频繁即便服务器无数据更新,也会产生大量请求,增加服务器和网络的负担,而且如果轮询间隔设置不合理,还可能出现数据更新的延迟(如图中 “延迟” 所示)。

长轮询

  • 工作机制:浏览器发送 Ajax 请求(如示例中的 GET/lpoll)后,服务器接收到请求会 “阻塞” 该请求。也就是说,服务器不会立即返回响应,而是等待有数据更新或者请求超时的时候,才会把数据返回给浏览器。
  • 特点:相比轮询,长轮询减少了不必要的请求次数。只有当有数据变化或者超时,服务器才会响应,能在一定程度上降低服务器和网络的压力,也能更及时地获取数据更新(只要数据更新在超时前发生)。

简单来说,轮询是 “定时问”,长轮询是 “问了等,有了才回”,二者在请求频率、资源消耗和数据实时性等方面存在差异,可根据实际场景选择使用。


1.2 SSE 技术

服务器发送事件(Server-Sent Events)是一种用于从服务器到客户端的 单向、实时 数据传输技术,基于 HTTP协议实现。

    它有几个重要的特点:

    1. 单向通信:SSE 只支持服务器向客户端的单向通信,客户端不能向服务器发送数据。
    2. 文本格式:SSE 使用 纯文本格式 传输数据,使用 HTTP 响应的 text/event-stream MIME 类型。(数据流信息)
    3. 保持连接:SSE 通过保持一个持久的 HTTP 连接,实现服务器向客户端推送更新,而不需要客户端频繁轮询。
    4. 自动重连:如果连接中断,浏览器会自动尝试重新连接,确保数据流的连续性。

    SSE 数据格式

    SSE 数据流的格式非常简单,使用 event 指定事件名称,用于区分不同类型的消息。每个事件使用 data 字段,作为消息主体,事件以两个换行符结束。还可以使用 id 字段来标识事件,并且 retry 字段可以设置重新连接的时间间隔。

    event: 事件名\n    // 可选,用于区分不同类型的消息
    data: 消息内容\n    // 必选,消息主体(可多行,每行以 data: 开头)
    id: 消息ID\n       // 可选,用于客户端记录最后接收的消息ID(重连时可通过 Last-Event-ID 头传递)
    retry: 重连时间(毫秒)\n  // 可选,指定客户端重连间隔
    \n  // 空行表示一条消息结束

    示例格式如下:

    data: Third message\n
    id: 3\n
    \n
    retry: 10000\n
    data: Fourth message\n
    \n

    1.3 WebSocket

    WebSocket 是一种网络通信协议,它通过在单个、长期的连接上提供全双工(双向)的通信通道,来解决 HTTP 协议在实时通信方面的不足。

    1.3.1 原理解析

    这张图解析了 WebSocket 连接的建立过程及原理,可分为以下几个部分:

    连接建立阶段(基于 HTTP 协议升级)

    1. 客户端请求:客户端向服务器发送一个特殊的 HTTP 请求,请求中包含 Upgrade: websocket 等头信息,表明希望将连接从 HTTP 协议升级为 WebSocket 协议。下方 “请求数据” 框里展示了具体的请求内容,像 GET ws://localhost/chat HTTP/1.1(指定 WebSocket 连接的地址)、Connection: Upgrade(表示要升级连接)、Upgrade: websocket(明确升级到 WebSocket 协议)等。
    2. 服务器响应:服务器收到请求后,返回 HTTP/1.1 101 Switching Protocols 响应,确认协议升级。下方 “响应数据” 框里呈现了响应的具体内容,包含 Upgrade: websocketConnection: Upgrade 等,表明已切换到 WebSocket 协议。

    数据传输阶段(基于 WebSocket 协议)

    当协议升级完成后,客户端和服务器就可以基于 WebSocket 协议进行全双工通信了,双方能相互主动发送数据(图中 “发送数据” 的双向箭头体现了这一点),不再受 HTTP 协议请求 - 响应模式的限制,适合实时性要求高的场景,比如在线聊天、实时数据推送等。

    1.3.2 客户端API


    1. WebSocket 对象创建

    代码示例:let ws = new WebSocket(URL);

    作用:在浏览器中创建一个 WebSocket 实例,用于和服务器建立 WebSocket 连接。

    URL 说明:

    • 格式为 协议://ip地址/访问路径
    • 其中协议名称为 ws(若为加密连接则用 wss,类似 HTTPS),ip地址 是服务器的地址,访问路径 是 WebSocket 服务在服务器上的具体路径。

    2. WebSocket 对象相关事件

    这部分通过表格列出了 WebSocket 实例常用的事件及对应的事件处理程序和描述:

    事件事件处理程序描述
    openws.onopen当客户端与服务器成功建立 WebSocket 连接时触发该事件。
    messagews.onmessage当客户端接收到服务器发送的数据时触发该事件。
    closews.onclose当 WebSocket 连接关闭时(无论是客户端主动关闭,还是服务器关闭,或者连接异常断开)触发该事件。

    3. WebSocket 对象提供的方法

    这部分介绍了 WebSocket 实例的方法:

    方法名称描述
    send()客户端通过调用 WebSocket 实例的 send() 方法,向服务器发送数据。

    1.3.3 代码实现
    <script>let ws = new WebSocket("ws://localhost/chat");ws.onopen = function() {};ws.onmessage = function(evt) {// 通过 evt.data 可以获取服务器发送的数据};ws.onclose = function() {};
    </script>

    1. 创建 WebSocket 实例

    let ws = new WebSocket("ws://localhost/chat");
    
    • 使用 new WebSocket() 构造函数创建一个 WebSocket 连接对象。
    • 参数 "ws://localhost/chat" 是 WebSocket 服务器的地址,ws:// 表示使用 WebSocket 协议(如果是加密的 WebSocket 连接,使用 wss://),localhost 是本地服务器地址,/chat 是具体的 WebSocket 端点路径,用于标识要连接的服务。

    2. 处理连接建立事件(onopen

    ws.onopen = function() {// 连接成功建立后执行的代码
    };
    
    • onopen 是 WebSocket 对象的一个事件处理属性。
    • 当客户端与 WebSocket 服务器成功建立连接时,这个函数会被调用。通常可以在这里执行一些连接成功后的操作,比如向服务器发送初始化消息等。

    3. 处理消息接收事件(onmessage

    ws.onmessage = function(evt) {// 通过 evt.data 可以获取服务器发送的数据
    };
    
    • onmessage 事件在客户端接收到服务器发送的数据时触发。
    • 回调函数的参数 evt 包含了服务器发送的数据,通过 evt.data 可以获取到具体的数据内容,数据可以是字符串、Blob 或者 ArrayBuffer 等格式,这里通常需要根据实际情况对数据进行解析和处理,比如展示聊天消息、更新实时数据等。

    4. 处理连接关闭事件(onclose

    ws.onclose = function() {// 连接关闭后执行的代码
    };
    

    • onclose 事件在 WebSocket 连接关闭时触发,不管是客户端主动关闭还是服务器关闭连接,或者连接异常断开,这个函数都会被调用。可以在这里进行一些资源清理、提示用户连接已关闭或者尝试重新连接等操作。

    1.3.4 服务端API

    Java WebSocket 应用由一系列的 Endpoint 组成。Endpoint 是一个 Java 对象,代表 WebSocket 链接的一端,对于服务端,我们可以视为处理具体 WebSocket 消息的接口。

    Endpoint 的定义方式

    我们可以通过两种方式定义 Endpoint

    • 第一种是编程式,即继承类 javax.websocket.Endpoint 并实现其方法。
    • 第二种是注解式,即定义一个 POJO,并添加 @ServerEndpoint 相关注解。

    Endpoint 的生命周期

    Endpoint 实例在 WebSocket 握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。在 Endpoint 接口中明确定义了与其生命周期相关的方法,规范实现者确保生命周期的各个阶段调用实例的相关方法。生命周期方法如下:

    方法描述注解
    onOpen()当开启一个新的会话时调用,该方法是客户端与服务端握手成功后调用的方法@OnOpen
    onClose()当会话关闭时调用@OnClose
    onError()当连接过程异常时调用@OnError

    1.3.5 服务端接收和推送数据

    1.3.6 代码实现

    @ServerEndpoint("/chat")
    @Component
    public class ChatEndpoint {@OnOpen//连接建立时被调用public void onOpen(Session session, EndpointConfig config) {}@OnMessage//接收到客户端发送的数据时被调用public void onMessage(String message) {}@OnClose//连接关闭时被调用public void onClose(Session session) {}
    }

    大功告成!

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如若转载,请注明出处:http://www.pswp.cn/news/921890.shtml
    繁体地址,请注明出处:http://hk.pswp.cn/news/921890.shtml
    英文地址,请注明出处:http://en.pswp.cn/news/921890.shtml

    如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    论文阅读:ACL 2024 Stealthy Attack on Large Language Model based Recommendation

    总目录 大模型相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 https://arxiv.org/pdf/2402.14836 https://www.doubao.com/chat/19815566713551106 文章目录速览攻击方法速览一、攻击核心目标与前提1. 核心目标2. 攻击前提二、模型无关的简单…

    自动驾驶中的传感器技术43——Radar(4)

    本文对目前毫米波雷达中的天线设计进行比较全面的罗列&#xff0c;并进行简单的设计评述 1、实际设计案例 图1 涵盖能宽窄覆盖的天线设计&#xff08;无俯仰分辨率&#xff09;图2 Bosch前雷达的天线设计&#xff08;有俯仰的分辨率但比较弱&#xff0c;也涵盖了扩展覆盖&…

    使用反转法线材质球,实现切换天空盒相同的功能,优点:包体变小

    切换天空盒第一步先把SKY 天空球资源导入到工程里&#xff0c; 第二步&#xff1a;天空球文件下的SKY预制件拖入到场景里 第三步 选着SKY材质球&#xff0c;拖入自己的全景图片(图片分辨率不能超过5000*5000&#xff0c;否则手机无法显示) 如果并没有效果&#xff0c;看看图…

    真正有效的数据指标体系应该长什么样?

    真正有效的数据指标体系应该长什么样&#xff1f;为什么大多数企业的指标体系都是"花架子"&#xff1f;真正有效的指标体系应该长什么样&#xff1f;从数据到洞察&#xff1a;让指标真正"活"起来结语在这个人人都在谈数字化转型的时代&#xff0c;企业就像…

    分布式专题——6 Redis缓存设计与性能优化

    1 多级缓存架构2 缓存设计 2.1 缓存穿透 2.1.1 简介缓存穿透是什么&#xff1f;当查询一个根本不存在的数据时&#xff0c;缓存层和存储层都不会命中。正常逻辑下&#xff0c;存储层查不到数据就不会写入缓存层。这会导致&#xff1a;每次请求这个不存在的数据&#xff0c;都要…

    一文了解大模型压缩与部署

    一文了解大模型压缩与部署&#xff1a;从 INT4 量化到 MoE&#xff0c;让大模型跑在手机、边缘设备和云端&#x1f3af; 为什么需要模型压缩与部署&#xff1f;你训练了一个强大的大模型&#xff08;如 Qwen-72B、LLaMA-3-70B&#xff09;&#xff0c;但在部署时发现&#xff1…

    新手向:中文语言识别的进化之路

    自然语言处理&#xff08;NLP&#xff09;技术正在以前所未有的速度改变我们与机器的交互方式。根据Gartner最新报告显示&#xff0c;全球NLP市场规模预计在2025年将达到430亿美元&#xff0c;年复合增长率高达21%。而中文作为世界上使用人数最多的语言&#xff08;全球约15亿使…

    LeetCode100-206反转链表

    本文基于各个大佬的文章上点关注下点赞&#xff0c;明天一定更灿烂&#xff01;前言Python基础好像会了又好像没会&#xff0c;所有我直接开始刷leetcode一边抄样例代码一边学习吧。本系列文章用来记录学习中的思考&#xff0c;写给自己看的&#xff0c;也欢迎大家在评论区指导…

    uniapp开源多商户小程序商城平台源码 支持二次开发+永久免费升级

    在电商行业竞争日益激烈的今天&#xff0c;拥有一个功能强大、灵活可拓展的多商户小程序商城至关重要。今天给大家分享一款 uniapp 开源多商户小程序商城平台源码&#xff0c;它不仅具备丰富的基础功能&#xff0c;还支持二次开发&#xff0c;更能享受永久免费升级服务&#xf…

    使用脚本一键更新NTP服务器地址为自定义地址

    【使用场景】 在银河麒麟桌面操作系统V10SP1-2303版本中使用脚本一键修改NTP服务器地址为自定义地址。 【操作步骤】 步骤1. 编写shell脚本 ```bash desktop2303@desktop2303-pc:~$ vim setntptimeserver.sh #!/bin/bashfunction modifykylinconf() { # 检查是否已存在目标配置…

    linux内核 - 内核架构概览

    当 Linux 系统启动时,内核会在启动过程的早期阶段接管控制——紧跟在固件(BIOS 或 UEFI)和引导加载程序完成任务之后。此时,压缩的 Linux 内核镜像会被加载到内存中,通常会附带一个称为 initramfs 的最小临时根文件系统,它用于在切换到真实根文件系统并继续系统初始化之前…

    [react] react-router-dom是啥?

    页面路由&#xff0c;注意页面路由不是路由器&#xff0c;因为我之前总是把路由和路由器搞混。而且我总是把前端页面的路由和路由器的路由搞混。那么这里一定要明白&#xff0c;这里我所说的页面路由就是指在浏览器里面的导航路由。 npm create vitelatest my-react-app – --t…

    HTTP简易客户端实现

    &#x1f310; HTTP简易客户端实现 流程图&#xff1a; 引用&#xff1a; chnroutes2.cpp#L474 chnroutes2_getiplist() chnroutes2.cpp#L443 http_easy_get(…) &#x1f552; 1. 超时管理机制 (http_easy_timeout) &#x1f539; 核心功能&#xff1a;创建定时器自动关…

    建筑面LAS点云高度计算工具

    效果 例如中位数,计算后,在shp建筑面中添加一个字段meidian_hei 准备数据 1、建筑矢量面.shp 2、点云.las 界面 脚本 import laspy import shapefile # pyshp库,处理POLYGONZ坐标格式异常 import pandas as pd import numpy as np import os import traceback # 打印…

    java day18

    继续学习&#xff0c;学习sringboot案例&#xff1b;熟悉的三件套&#xff1b;比如做一个表&#xff0c;前端搭建好框架&#xff0c;然后返回给后端一个请求&#xff0c;说要这个表的数据吧&#xff1b;然后通过请求和规定的格式返回给后端之后&#xff0c;我们后端进行接收处理…

    并发编程原理与实战(二十八)深入无锁并发演进,AtomicInteger核心API详解与典型场景举例

    无锁并发演进背景 随着系统高并发的压力越来越大&#xff0c;传统同步机制在高并发场景下的性能瓶颈和缺点可能会逐渐显露&#xff1a; &#xff08;1&#xff09;性能损耗&#xff1a;synchronized等锁机制会导致线程阻塞和上下文切换&#xff0c;在高并发场景下性能损耗显著。…

    整体设计 之 绪 思维导图引擎 之 引 认知系统 之 引 认知系统 之 序 认知元架构 之5 : Class 的uml profile(豆包助手 之7)

    摘要&#xff08;AI生成&#xff09;三层中间件架构的约束逻辑体系1. 架构定位与功能分工三个中间层&#xff08;隔离层/隐藏层/防腐层&#xff09;构成数据处理管道&#xff0c;分别承担&#xff1a;隔离层&#xff1a;跨系统数据转换处理对象&#xff1a;异构数据&#xff08…

    iframe引入界面有el-date-picker日期框,点击出现闪退问题处理

    前言&#xff1a;iframe引入界面有el-date-picker日期框&#xff0c;点击出现闪退问题处理。问题情况&#xff1a;点击开始日期的输入部分&#xff0c;会出现闪退情况&#xff0c;该组件是iframe调用的内容问题分析&#xff1a;事件冒泡&#xff0c;点击与聚焦的时候&#xff0…

    docker 拉取本地镜像

    要在Docker中拉取本地镜像&#xff0c;通常有以下几种实现方法&#xff1a; 使用docker pull命令&#xff1a;可以使用docker pull命令从本地镜像仓库拉取镜像。例如&#xff0c;如果本地镜像的名称是my-image&#xff0c;则可以运行以下命令拉取镜像&#xff1a; docker pull …

    嘉立创EDA从原理图框选住器件进行PCB布局

    1、先选中需要布局的模块的相关器件2、设计-》布局传递3、在PCB会选中模块相关的元器件&#xff0c;拖动进行布局4、依次将每个模块都分类出来5、板框设计&#xff1a;如果有要求大小&#xff0c;可以先将单位设置为mm&#xff0c;然后画出来板框的尺寸