[Java] 服务端消息推送汇总

前言:当构建实时消息推送功能时,选择适合的方案对于开发高效的实时应用至关重要。消息的推送无非就推、拉两种数据模型。本文将介绍四种常见的消息实时推送方案:短轮询(拉)、长轮训(拉)、SSE(Server-Sent Events)(推)和WebSocket(推),并以Spring Boot作为技术底座,展示如何在Java全栈开发中实现这些功能。


文章目录

    • 1. 短轮询(Short Polling)
      • 什么是短轮询?
      • 短轮询的实现
      • 短轮询的特点与限制
    • 2. 长轮询(Long Polling)
      • 什么是长轮询?
      • 长轮询的实现
      • 长轮询的特点与限制
    • 3. SSE(Server-Sent Events)
      • 什么是SSE?
      • SSE的实现
      • SSE的特点与限制
    • 4. WebSocket
      • 什么是WebSocket?
      • WebSocket的实现
        • 1. 创建一个WebSocket处理器:
        • 2. 配置WebSocket端点:
        • 3. 前端实现
      • WebSocket的特点与限制
  • 总结
  • 注意


1. 短轮询(Short Polling)

什么是短轮询?

短轮询是一种简单的实时消息推送方案,其中客户端通过定期向服务器发送请求来获取最新的消息。服务器在接收到请求后立即响应,无论是否有新消息。如果服务器没有新消息可用,客户端将再次发送请求。

短轮询的实现

在Spring Boot中,可以通过HTTP接口和定时任务来实现短轮询。下面是一个简单的示例:

// -- 后端接口
@GetMapping("/short")
public String  getShort() {long l = System.currentTimeMillis();if((l&1) == 1) {return "ok";}return "fail";
}// --- 前端页面
@org.springframework.stereotype.Controller
public class Controller {@GetMapping("/s")public String s() {return "s";}
}// -- s.html<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>短轮询</title>
</head>
<body>
<p>msg=<span id="message"></span></p>
<script>function pollMessage() {
// 发送轮询请求const xhr = new XMLHttpRequest();xhr.open("GET", "/short", true);xhr.onreadystatechange = function () {if (xhr.readyState === XMLHttpRequest.DONE) {if (xhr.status === 200) {document.getElementById("message").innerHTML = xhr.responseText;}}};xhr.send();}setInterval(()=>{pollMessage()}, 1000)</script>
</body>
</html>

在上述示例中,getShort()方法用于返回消息,而s方法用于渲染s.html。客户端可以定期调用getShort()接口来获取最新的消息。

短轮询的特点与限制

短轮询的实现简单,但存在一些特点和限制:

  • 高延迟: 客户端需要定期发送请求,无论是否有新消息。这会导致一定的延迟,特别是在消息更新较慢的情况下。
  • 高网络负载: 客户端需要频繁发送请求,即使消息没有更新。这会增加服务器和网络的负载。
  • 实时性差: 由于需要等待下一次轮询才能获取新消息,短轮询的实时性相对较差。

2. 长轮询(Long Polling)

什么是长轮询?

长轮询是改进的轮询方法,它在没有新消息时会保持请求挂起,直到有新消息到达或超时。相比于短轮询,长轮询可以更快地获取新消息,减少了不必要的请求。

长轮询的实现

在Spring Boot中,可以使用异步请求和定时任务来实现长轮询。下面是一个简单的示例:

// -- 请求接口/*** 长轮询* @return*/
@GetMapping("/long")
public DeferredResult<String> getLong() {DeferredResult<String> deferredResult = new DeferredResult<>();if (latestMessage != null) {deferredResult.setResult(latestMessage);} else {// 使用定时任务设置超时时间TimerTask timeoutTask = new TimerTask() {@Overridepublic void run() {deferredResult.setResult(null);}};Timer timer = new Timer();timer.schedule(timeoutTask, 5000); // 设置超时时间为5秒// 设置回调函数,在消息到达时触发deferredResult.onTimeout(() -> {timer.cancel();deferredResult.setResult(null);});deferredResult.onCompletion(timer::cancel);}return deferredResult;
}/*** 设置消息* @param message*/
@PostMapping("/send-message")
public void sendMessage(@RequestBody String message) {latestMessage = message;
}// -- 前端请求<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>长轮询</title>
</head>
<body>
<p>msg=<span id="message"></span></p>
<p>请求次数:<span id="cnt"></span></p>
<script>var cnt = 0function pollMessage() {
// 发送轮询请求const xhr = new XMLHttpRequest();xhr.open("GET", "/long", true);xhr.onreadystatechange = function () {if (xhr.readyState === XMLHttpRequest.DONE) {if (xhr.status === 200) {document.getElementById("message").innerHTML = xhr.responseText;}}};xhr.send();}setInterval(()=>{++cnt;document.getElementById('cnt').innerHTML = cnt.toString()pollMessage()}, 5000)</script>
</body>
</html>

在上述示例中,getLong()方法返回一个DeferredResult对象,它会在有新消息到达时触发回调函数。如果在超时时间内没有新消息到达,DeferredResult对象将返回null

长轮询的特点与限制

长轮询具有以下特点与限制:

  • 减少请求次数: 长轮询可以更快地获取新消息,相比于短轮询,可以减少不必要的请求次数。
  • 减少网络负载: 当没有新消息时,长轮询会保持请求挂起,减少了频繁的请求,从而减轻了服务器和网络的负载。
  • 相对实时性提升: 长轮询可以更快地获取新消息,相比于短轮询,实时性有所提升。然而,仍然需要等待下一次轮询才能获取新消息。

3. SSE(Server-Sent Events)

在这里插入图片描述

什么是SSE?

当使用Server-Sent Events(SSE)时,客户端(通常是浏览器)与服务器之间建立一种持久的连接,使服务器能够主动向客户端发送数据。这种单向的、服务器主动推送数据的通信模式使得实时更新的数据能够被实时地传送到客户端,而无需客户端进行轮询请求。

SSE的工作原理如下:

  1. 建立连接:客户端通过使用EventSource对象在浏览器中创建一个与服务器的连接。客户端向服务器发送一个HTTP请求,请求的头部包含Accept: text/event-stream,以表明客户端希望接收SSE数据。服务器响应这个请求,并建立一个持久的HTTP连接。
  2. 保持连接:服务器保持与客户端的连接打开状态,不断发送数据。这个连接是单向的,只允许服务器向客户端发送数据,客户端不能向服务器发送数据。
  3. 服务器发送事件:服务器使用Content-Type: text/event-stream标头来指示响应是SSE数据流。服务器将数据封装在特定的SSE格式中,每个事件都以data:开头,后面是实际的数据内容,以及可选的其他字段,如event:id:。服务器发送的数据可以是任何文本格式,通常是JSON。
  4. 客户端接收事件:客户端通过EventSource对象监听服务器发送的事件。当服务器发送事件时,EventSource对象会触发相应的事件处理程序,开发人员可以在处理程序中获取到事件数据并进行相应的操作。常见的事件是message事件,表示接收到新的消息。
  5. 断开连接:当客户端不再需要接收服务器的事件时,可以关闭连接。客户端可以调用EventSource对象的close()方法来显式关闭连接,或者浏览器在页面卸载时会自动关闭连接。

SSE的实现

在Spring Boot中,可以使用SseEmitter类来实现SSE。下面是一个简单的示例:

@RestController
public class SSEController {private SseEmitter sseEmitter;@GetMapping("/subscribe")public SseEmitter subscribe() {sseEmitter = new SseEmitter();return sseEmitter;}@PostMapping("/send-message")public void sendMessage(@RequestBody String message) {try {if (sseEmitter != null) {sseEmitter.send(SseEmitter.event().data(message));}} catch (IOException e) {e.printStackTrace();}}
}// - s.html<!DOCTYPE html>
<html>
<head><title>SSE Demo</title>
</head>
<body>
<h1>SSE Demo</h1>
<div id="message-container"></div><script>// 创建一个EventSource对象,指定SSE的服务端端点var eventSource = new EventSource('/subscribe');console.log("eventSource=", eventSource)// 监听message事件,接收从服务端发送的消息eventSource.addEventListener('message', function(event) {var message = event.data;console.log("message=", message)var messageContainer = document.getElementById('message-container');messageContainer.innerHTML += '<p>' + message + '</p>';});
</script>
</body>
</html>

在上述示例中,客户端可以通过访问/subscribe接口来订阅SSE事件,服务器会返回一个SseEmitter对象。当有新消息到达时,调用SseEmitter对象的send()方法发送消息。

在这里插入图片描述

SSE的特点与限制

SSE具有以下特点与限制:

  • 实时性较好: SSE使用了持久连接,可以实现比短轮询和长轮询更好的实时性。
  • 单向通信: SSE是单向的,只允许服务器向客户端推送消息,客户端无法向服务器发送消息。
  • 不适用于低版本浏览器: SSE是HTML5的一部分,不支持低版本的浏览器。在使用SSE时,需要确保客户端浏览器的兼容性。

4. WebSocket

在这里插入图片描述

什么是WebSocket?

WebSocket是一种双向通信协议,允许在单个持久连接上进行全双工通信。与之前介绍的方案不同,WebSocket提供了双向通信的能力,可以实现实时的双向数据传输。

WebSocket的实现

在Spring Boot中,可以使用Spring WebSocket模块来实现WebSocket功能。下面是一个简单的示例:

1. 创建一个WebSocket处理器:
@Component
public class WebSocketHandler extends TextWebSocketHandler {private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {sessions.add(session);}@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {for (WebSocketSession webSocketSession : sessions) {webSocketSession.sendMessage(message);}}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {sessions.remove(session);}
}
2. 配置WebSocket端点:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Autowiredprivate WebSocketHandler webSocketHandler;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(webSocketHandler, "/websocket").setAllowedOrigins("*");}
}

在上述示例中,WebSocketHandler处理器负责处理WebSocket连接、消息传递和连接关闭等事件。WebSocketConfig类用于配置WebSocket端点。

3. 前端实现
<!DOCTYPE html>
<html>
<head><title>WebSocket Demo</title>
</head>
<body>
<h1>WebSocket Demo</h1>
<div id="message-container"></div><script>// 创建WebSocket对象,并指定服务器的URLvar socket = new WebSocket('ws://localhost:8080/websocket');// 监听WebSocket的连接事件socket.onopen = function(event) {console.log('WebSocket connected');};// 监听WebSocket的消息事件socket.onmessage = function(event) {var message = event.data;var messageContainer = document.getElementById('message-container');messageContainer.innerHTML += '<p>' + message + '</p>';};// 监听WebSocket的关闭事件socket.onclose = function(event) {console.log('WebSocket closed');};// 发送消息到服务器function sendMessage() {var messageInput = document.getElementById('message-input');var message = messageInput.value;socket.send(message);messageInput.value = '';}
</script><input type="text" id="message-input" placeholder="Enter message">
<button onclick="sendMessage()">Send</button>
</body>
</html>

在这里插入图片描述

WebSocket的特点与限制

WebSocket具有以下特点与限制:

  • 实时性最佳: WebSocket 提供了真正的双向通信,可以实现实时的双向数据传输,具有最佳的实时性。
  • 低延迟: 与轮询和长轮询相比,WebSocket 使用单个持久连接,减少了连接建立和断开的开销,从而降低了延迟。
  • 双向通信: WebSocket 允许服务器与客户端之间进行双向通信,服务器可以主动向客户端发送消息,同时客户端也可以向服务器发送消息。
  • 较高的网络负载: WebSocket 使用长连接,会占用一定的网络资源。在大规模并发场景下,需要注意服务器的负载情况。
  • 浏览器支持: 大多数现代浏览器都支持 WebSocket,但需要注意在开发过程中考虑不同浏览器的兼容性。

总结

本文介绍了四种常见的消息实时推送方案:短轮询、长轮询、SSE WebSocket,并以 Spring Boot 作为技术底座,展示了如何在 Java 全栈开发中实现这些功能。

  • 短轮询是一种简单的实时消息推送方案,但存在高延迟、高网络负载和实时性差的限制。
  • 长轮询通过保持请求挂起来减少不必要的请求次数,提高了实时性,但仍需要轮询才能获取新消息。
  • SSE 使用持久连接实现单向实时消息推送,具有较好的实时性,但只支持服务器向客户端的单向通信。
  • WebSocket 提供了真正的双向通信,具有最佳的实时性和低延迟,但需要注意较高的网络负载和浏览器兼容性。

选择合适的消息实时推送方案取决于具体的需求和场景。根据应用程序的要求和预期的用户体验,开发人员可以选择适当的方案来实现实时消息推送功能。

注意

以上实现均属于demo 级别,为了简单演示,将所有的服务保证措施都删除了,所以存在包括但不限于以下缺点

比如

  • sse
  1. 没有做会话管理
  2. 明文传输
  • WebSocket
  1. 客户端连接的区分,目前的实现属于消息广播。

  2. 连接的可靠性保证:心跳检测以及自动重连等。

  3. 消息的明文传输

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

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

相关文章

c++视觉处理---高斯滤波

高斯滤波处理 高斯滤波是一种常用的平滑滤波方法&#xff0c;它使用高斯函数的权重来平滑图像。高斯滤波通常用于去除噪声并保留图像中的细节。在OpenCV中&#xff0c;可以使用cv::GaussianBlur()函数来应用高斯滤波。 以下是cv::GaussianBlur()函数的基本用法&#xff1a; …

vue实现echarts中 9种 折线图图例

let datas [{ DivideScore: 7, UserScore: 7.2, Name: 目标制定 },{ DivideScore: 7, UserScore: 7, Name: 具体性 },{ DivideScore: 7, UserScore: 7.5, Name: 可衡量性 },{ DivideScore: 7, UserScore: 7, Name: 可实现性 },{ DivideScore: 7, UserScore: 7, Name: 时间限定…

简单强大的时序图绘制工具

今天分享一个简单强大的时序图绘制工具——WaveDrom。 WaveDrom Digital Timing Diagram everywhere WaveDrom draws your Timing Diagram or Waveform from simple textual description. It comes with description language, rendering engine and the editor. WaveDrom edi…

基于Springboot实现房屋租赁租房平台系统项目【项目源码+论文说明】

基于Springboot实现房屋租赁租房平台系统演示 摘要 在网络高速发展的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;房东只能以用户为导向&#xff0c;所以开发租房网…

MongoDB-介绍与安装部署

介绍与安装部署 1.MongoDB简介a) 体系结构b) 数据模型c) MongoDB的特点c.1) 高性能c.2) 高性可用性c.3) 高拓展性c.4) 丰富的查询支持 2.单机部署a) Windows系统中的安装启动b) Shell连接(mongo命令)c) Linux系统中的安装启动和连接 1.MongoDB简介 MongoDB是一个开源、高性能、…

【网络安全入门】学习网络安全必须知道的100 个网络基础知识

前言 话不多说&#xff0c;完整的资料已经上传至CSDN官方&#xff0c;需要的可以点击链接自取【282G】网络安全&黑客技术零基础到进阶全套学习大礼包&#xff0c;免费分享&#xff01; 1 什么是链接? 链接是指两个设备之间的连接。它包括用于一个设备能够与另一个设备…

k8s containerd查看镜像

直接查看crictl image会报错&#xff1a; 1) crictl config runtime-endpoint unix:///run/containerd/containerd.sock 2) vi /etc/crictl.yaml 3) systemctl daemon-reload 此时&#xff0c;再查看image:

Python —— UI自动化之八大元素定位

1、基础元素定位 1、id定位 使用html中标签的id元素去定位&#xff0c;在一般定位中优先选择&#xff0c;举例&#xff1a; from time import sleep from selenium import webdriver from selenium.webdriver.common.by import Bydriver webdriver.Firefox() driver.get(&q…

CI/CD工具中的CI和CD的含义

CI/CD工具中的CI和CD的含义&#xff1f; CI/CD 是现代软件开发方法中广泛使用的一种方法。其中&#xff0c;CI 代表持续集成&#xff08;Continuous Integration&#xff09;&#xff0c;CD 则有两层含义&#xff0c;一是持续交付&#xff08;Continuous Delivery&#xff09;…

Pyside6 QPushButton

Pyside6 QPushButton QPushButton使用QPushButton继承关系QPushButton的函数(Function)和信号(Signal)QPushButton信号 QPushButton例程界面设计clicked信号测试pressed信号测试released信号测试toggled信号测试按键长按测试按键长按间隔测试完整程序界面程序主程序 按键或命令…

redis学习(二)——redis常见命令及基础数据类型

数据类型 基础数据类型 字符串 String abcMap集合 Hsah {name:“zhangsan”,age:18}列表 List [a, b, c, d]Set集合 Set {a,b,c}有序Set集合 SortSet {a:1,b:2,c:3} 特殊数据类型 GEO 地理坐标 {A:(100.2,35.1)}BitMap 位图&#xff0c;只存储0和1 01101011101HyperLog 基数…

详解CAN通信的标识符掩码和标识符列表两种过滤机制

CAN 通信的应用非常广泛&#xff0c;本文不涉及CAN通信的基础配置&#xff0c;重点分析一下STM32和GD32的CAN通信两种ID过滤方式。 首先&#xff0c;不管是STM32还是GD32&#xff0c;实现CAN通信ID过滤的机制和原理一定是一样的&#xff0c;只是用到的寄存器有差别。 1. ID过…

计算机视觉简介(1)

任何计算机视觉处理流程都始于成像系统&#xff0c;它从景物中捕获反射出来的光线&#xff0c;并将光信号转换成计算机可以读取和处理的图像格式 在计算机成像技术发展的早期&#xff0c;图像通过把胶卷或印刷图像素 化后获得&#xff1b;而现在图 像通常直接由数码相机获取&a…

消息驱动 —— SpringCloud Stream

Stream 简介 Spring Cloud Stream 是用于构建消息驱动的微服务应用程序的框架&#xff0c;提供了多种中间件的合理配置 Spring Cloud Stream 包含以下核心概念&#xff1a; Destination Binders&#xff1a;目标绑定器&#xff0c;目标指的是 Kafka 或者 RabbitMQ&#xff0…

记录一次springboot使用定时任务中@Async没有生效的场景

环境说明 jdk21springboot 3.0.11 springcloud 2022.0.0 spring-cloud-alibaba 2022.0.0.0 在开发一个定时触发的任务的时候&#xff0c;由于开发执行任务的函数比较耗费时间&#xff0c;所以采用异步解决问题。 发现并没有按照预期的触发 经询问后&#xff0c;发现当前类的…

2023年中国隆鼻行业发展历程及趋势分析:隆鼻手术市场将实现进一步增长[图]

隆鼻术就是以各种植入材料置入为主要方法&#xff0c;隆起或抬高鼻部形态为主要目的的鼻整形术式。隆鼻术可能是开展最多的整形美容手术之一。隆鼻术也是一种很成熟的美容手术&#xff0c;操作较为简单、安全、风险较小&#xff0c;也易于接受。 隆鼻行业分类 资料来源&#x…

这道面试题工作中经常碰到,但 99% 的程序员都答不上来

小时候都被问过一个脑筋急转弯&#xff0c;把大象放进冰箱有几个步骤&#xff1f;我们一开始都会抓耳挠腮&#xff0c;去想着该如何把大象塞进冰箱。最终揭晓的答案却根本不关心具体的操作方法&#xff0c;只是提供了 3 个步骤组成的流程&#xff0c;「把冰箱打开&#xff0c;把…

Notepad++提取含有特定字符串的行

ctrl M快捷键&#xff0c;进入"标记" 页面 标记所在行–循环查找-- 正则表达式 – 输入关键字 – 全部标记 – Copy Marked Text 关键字格式如下&#xff1a; .*关键字.*ctrl v&#xff0c;粘贴即可。

深度学习基础知识 nn.Sequential | nn.ModuleList | nn.ModuleDict

深度学习基础知识 nn.Sequential &#xff5c; nn.ModuleList &#xff5c; nn.ModuleDict 1、nn.Sequential 、 nn.ModuleList 、 nn.ModuleDict 类都继承自 Module 类。2、nn.Sequential、nn.ModuleList 和 nn.ModuleDict语法3、Sequential 、ModuleDict、 ModuleList 的区别…

阿里云数据库MongoDB恢复到本地

共两种方式&#xff0c;建议使用第二种的逻辑恢复&#xff0c;比较方便快捷 一、下载物理备份文件 下载的格式是xb的&#xff0c;主要跟实例创建时间有关&#xff0c;2019年03月26日之前创建的实例&#xff0c;物理备份文件格式为tar&#xff0c;后面全部都是xb的格式了&#…