前端订阅后端推送WebSocket定时任务
0.需求
后端定时向前端看板推送数据,每10秒或者30秒推送一次。
1.前言知识
HTTP协议是一个应用层协议,它的特点是无状态、无连接和单向的。在HTTP协议中,客户端发起请求,服务器则对请求进行响应。这种请求-响应的模式意味着服务器无法主动向客户端发送消息。
这种单向通信的缺点在于,如果服务器有持续的状态变化,客户端要获取这些变化就很困难。为了解决这个问题,许多Web应用采用了一种叫做长轮询的技术,即频繁地通过AJAX和XML发起异步请求来检查服务器的状态。但这种方式效率较低,也很浪费资源,因为需要不断地建立连接或保持连接打开。
而WebSocket则是一种不同的通信协议,它允许客户端和服务器之间进行全双工通信。这意味着无论是客户端还是服务器,都可以随时通过已经建立的连接向对方发送数据。而且,WebSocket只需要建立一次连接就可以保持通信状态,无需频繁地建立和断开连接,因此效率大大提高。
总结一下,HTTP协议虽然广泛应用,但因其单向通信的局限性,在处理服务器状态持续变化的情况时显得力不从心。而WebSocket协议则通过全双工通信的方式,有效地解决了这个问题,提高了通信效率。
2.后端实现
2.1不带参数
2.1.1添加依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
2.1.2websocket配置:
/*** 通过EnableWebSocketMessageBroker* 开启使用STOMP协议来传输基于代理(message broker)的消息,* 此时浏览器支持使用@MessageMapping 就像支持@RequestMapping一样。*///WebSocket的配置类
@Configuration
//开启对WebSocket的支持
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer{/*** 注册stomp的端点* 注册一个STOMP协议的节点,并映射到指定的URL*/@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {//endPoint 注册协议节点,并映射指定的URl点对点-用//注册一个名字为"/endpointSocket" 的endpoint,并指定 SockJS协议。//允许使用socketJs方式访问,访问点为webSocketServer,允许跨域//连接前缀//配置客户端尝试连接地址//广播registry.addEndpoint("/ws/public").setAllowedOriginPatterns("*").withSockJS();//点对点registry.addEndpoint("/ws/private").setAllowedOriginPatterns("*").withSockJS();}/*** 通过实现 WebSocketMessageBrokerConfigurer 接口和加上 @EnableWebSocketMessageBroker 来进行 stomp 的配置与注解扫描。* 其中覆盖 registerStompEndpoints 方法来设置暴露的 stomp 的路径,其它一些跨域、客户端之类的设置。* 覆盖 configureMessageBroker 方法来进行节点的配置。* 其中 enableSimpleBroker配置的广播节点,也就是服务端发送消息,客户端订阅就能接收消息的节点。* 覆盖setApplicationDestinationPrefixes方法,设置客户端向服务端发送消息的节点。* 覆盖 setUserDestinationPrefix 方法,设置一对一通信的节点。** @param registry*/@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {//配置消息代理,即设置广播节点registry.enableSimpleBroker("/topic","/user");//后端接收的主题前缀,即客户端向服务端发送消息需要有/client前缀
// registry.setApplicationDestinationPrefixes("/client");//指定用户发送(一对一)的前缀/user/
// registry.setUserDestinationPrefix("/user/");}
}
2.1.3后端代码
一个是订阅请求接口,一个是关闭定时任务接口。这段代码实现了一个基于WebSocket的定时推送机制,允许通过发送WebSocket消息来启动和关闭定时任务,从而每30秒推送一次数据。
/*** 看板接口-不带参数* 定时任务(每30秒推送一次)*/@MessageMapping("/backend/produce/summary")public void pushProduceSummary() {log.info("服务端接收到消息: {}");if (scheduledTask.get("pushProduceSummary") == null) {ScheduledFuture<?> future = executorService.scheduleAtFixedRate(() -> {ProgressVO progressVO = progressSummaryService.summary();String destination = "/topic/backend/produce/summary";template.convertAndSend(destination, progressVO);log.info("已推送信息,每30秒推送一次:{}");}, 1, 30, TimeUnit.SECONDS);scheduledTask.put("pushProduceSummary", future);} else {log.info("定时任务已开始!");}}
/*** 关闭/backend/produce/summary接口的定时任务** @author weiq*/@MessageMapping("/close/backend/produce/summary")public void cancelPushProduceSummary() {scheduledTask.forEach((StringKey, future) -> {if (future != null && !future.isCancelled() && StringKey.equals("pushProduceSummary")) {// 清除定时任务的引用scheduledTask.remove("pushProduceSummary");boolean cancel = future.cancel(true);if (cancel) {log.info("已关闭定时任务Key={}",StringKey);}else{log.info("失败关闭定时任务Key={}",StringKey);}}});}
2.2带参数
一个是订阅请求接口,一个是关闭定时任务接口。
- 当客户端向
/backend/produce/runEfficiency/{startTime}/{endTime}
这个 WebSocket 地址发送消息时,pushProduceRunEfficiency
方法会被调用。 - 这个方法会检查是否已有一个定时任务在运行。如果没有,它会创建一个新的定时任务,该任务会每30秒从
runEfficiencyService
获取运行效率数据,并通过 WebSocket 发送到指定的主题(destination
)。 - 前端(或任何监听该主题的 WebSocket 客户端)需要事先订阅这个主题,以便能够接收后端发送的数据。
/*** (看板)*定时任务(每30秒推送一次)* @param startTime* @param endTime*/@MessageMapping("/backend/produce/runEfficiency/{startTime}/{endTime}")public void pushProduceRunEfficiency(@DestinationVariable Long startTime, @DestinationVariable Long endTime) {log.info("服务端接收到消息: startTime={},endTime={}", startTime, endTime);if (scheduledTask.get("pushProduceRunEfficiency") == null) {ScheduledFuture<?> future = executorService.scheduleAtFixedRate(() -> {List<RunVO> runVOList = runEfficiencyService.run(startTime, endTime);String destination = "/topic/backend/produce/runEfficiency" + "/" + startTime + "/" + endTime;template.convertAndSend(destination, runVOList);log.info("已推送信息,每30秒推送一次:{}");}, 1, 30, TimeUnit.SECONDS);scheduledTask.put("pushProduceRunEfficiency", future);}else{log.info("定时任务已开启!");}}/*** 关闭/backend/produce/runEfficiency/{startTime}/{endTime}接口的定时任务** @author weiq*/@MessageMapping("/close/backend/produce/runEfficiency")public void cancelPushProduceRunEfficiency() {scheduledTask.forEach((StringKey, future) -> {if (future != null && !future.isCancelled() && StringKey.equals("pushProduceRunEfficiency")) {// 清除定时任务的引用scheduledTask.remove("pushProduceRunEfficiency");boolean cancel = future.cancel(true);if (cancel) {log.info("已关闭定时任务Key={}",StringKey);} else {log.info("失败定时任务Key={}",StringKey);}}});}
3.前端验证
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script><script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script><script src="https://code.jquery.com/jquery-3.2.0.min.js"integrity="sha256-JAW99MJVpJBGcbzEuXk4Az05s/XyDdBomFqNlM3ic+I=" crossorigin="anonymous"></script><script type="text/javascript">var stompClient = null;function setConnected(connected) {document.getElementById("connect").disabled = connected;document.getElementById("disconnect").disabled = !connected;$("#response").html();}function connect() {console.log("开始连接吧")var socket = new SockJS("http://localhost:8501/ws/public");stompClient = Stomp.over(socket);stompClient.connect({}, function (frame) {setConnected(true);console.log('Connected: ' + frame);//前端连接完成后,开始订阅主题// stompClient.subscribe('/topic/all', function (response) {stompClient.subscribe('/topic/backend/produce/summary', function (response) {var responseData = document.getElementById('responseData');var p = document.createElement('p');p.style.wordWrap = 'break-word';p.appendChild(document.createTextNode(response.body));responseData.appendChild(p);});}, {});}function disconnect() {if (stompClient != null) {stompClient.disconnect();}setConnected(false);console.log("Disconnected");}//请求地址,向WebSocket 地址发送消息function sendMsg() {var content = document.getElementById('content').value;// stompClient.send("/all", {}, JSON.stringify({'content': content}));stompClient.send("/backend/produce/summary", {}, JSON.stringify({'content': content }));}//关闭WebSocket 请求的定时任务function sendMsg1() {var content = document.getElementById('content').value;// stompClient.send("/all", {}, JSON.stringify({'content': content}));stompClient.send("/close/backend/produce/summary", {}, JSON.stringify({'content': content }));}// function sendMsg1() {// var content = document.getElementById('content').value;// // stompClient.send("/all", {}, JSON.stringify({'content': content}));// stompClient.send("/close/scene/stepActualTime/128", {}, JSON.stringify({'content': content }));// }//// function sendMsg2() {// var content = document.getElementById('content').value;// // stompClient.send("/all", {}, JSON.stringify({'content': content}));// stompClient.send("/close/scene/stepActualTime/219", {}, JSON.stringify({'content': content }));// }</script>
</head><body notallow="disconnect()">
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript beingenabled. Please enableJavascript and reload this page!</h2>
</noscript>
<div><div><labal>连接广播频道</labal><button id="connect" onclick="connect()">Connect</button><labal>取消连接</labal><button id="disconnect" disabled="disabled" onclick="disconnect()">Disconnect</button></div><div id="conversationDiv"><labal>广播消息</labal><input type="text" id="content"/><button id="sendMsg" onclick="sendMsg();">Send</button></div><div id="conversationDiv1"><labal>广播消息1</labal><input type="text" id="content1"/><button id="sendMsg1" onclick="sendMsg1();">Send</button></div><!-- <div id="conversationDiv2">-->
<!-- <labal>广播消息2</labal>-->
<!-- <input type="text" id="content2"/>-->
<!-- <button id="sendMsg2" onclick="sendMsg2();">Send</button>--><!-- </div>--><div><labal>接收到的消息:</labal><p id="responseData"></p></div>
</div></body>
</html>
后端启动,打开HTML测试页面,可看到运行结果!
相关文章:

前端订阅后端推送WebSocket定时任务
0.需求 后端定时向前端看板推送数据,每10秒或者30秒推送一次。 1.前言知识 HTTP协议是一个应用层协议,它的特点是无状态、无连接和单向的。在HTTP协议中,客户端发起请求,服务器则对请求进行响应。这种请求-响应的模式意味着服务器…...
提高机器人系统稳定性:引入阻尼作为共振后的相位超前
在机器人关节中,引入阻尼作为共振后的相位超前,确实是一种提高系统稳定性的有效策略。机器人关节的振动和共振是影响其性能稳定性的关键因素,特别是在进行高速、高精度操作时。阻尼的引入能够显著减少这些不利因素,提升机器人的整…...

深度学习理论基础(三)封装数据集及手写数字识别
目录 前期准备一、制作数据集1. excel表格数据2. 代码 二、手写数字识别1. 下载数据集2. 搭建模型3. 训练网络4. 测试网络5. 保存训练模型6. 导入已经训练好的模型文件7. 完整代码 前期准备 必须使用 3 个 PyTorch 内置的实用工具(utils): ⚫…...

vue3+eachrts饼图轮流切换显示高亮数据
<template><div class"charts-box"><div class"charts-instance" ref"chartRef"></div>// 自定义legend 样式<div class"charts-note"><span v-for"(items, index) in data.dataList" cla…...

UTONMOS:AI+Web3+元宇宙数字化“三位一体”将触发经济新爆点
人工智能、元宇宙、Web3,被称为数字化的“三位一体”,如何看待这三大技术所扮演的角色? 3月24日,2024全球开发者先锋大会“数字化的三位一体——人工智能、元宇宙、Web3.0”论坛在上海漕河泾开发区举行,首次提出&…...
开始焦虑了
大家好,我是洋子,25届的暑期实习自从3月份开始招聘有一段时间了,最近接到了几个25届同学的咨询,其中一个学妹印象比较深刻,她的情况如下 个人情况 学历是双非本,计算机专业,学习方向是Java&…...

数据结构和算法:十大排序
排序算法 排序算法用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用,因为有序数据通常能够被更高效地查找、分析和处理。 排序算法中的数据类型可以是整数、浮点数、字符或字符串等。排序的判断规则可根据需求设定,如数字大小、字符 ASCII…...

LLaMA-Factory微调(sft)ChatGLM3-6B保姆教程
LLaMA-Factory微调(sft)ChatGLM3-6B保姆教程 准备 1、下载 下载LLaMA-Factory下载ChatGLM3-6B下载ChatGLM3windows下载CUDA ToolKit 12.1 (本人是在windows进行训练的,显卡GTX 1660 Ti) CUDA安装完毕后,…...

Web安全-浏览器安全策略及跨站脚本攻击与请求伪造漏洞原理
Web安全-浏览器安全策略及跨站脚本攻击与请求伪造漏洞原理 Web服务组件分层概念 静态层 :web前端框架:Bootstrap,jQuery,HTML5框架等,主要存在跨站脚本攻击脚本层:web应用,web开发框架,web服务…...
蓝桥杯B组C++省赛——飞机降落(DFS)
题目连接:https://www.lanqiao.cn/problems/3511/learning/ 思路:由于数据范围很小,所有选择用DFS枚举所有飞机的所有的降落顺序,看哪个顺序可以让所有飞机顺利降落,有的话就算成功方案,输出了“YES”。 …...
Java 中的 Map集合
文章目录 添加和修改元素获取元素检查元素删除元素获取所有键 / 值 / 键值对大小 在 Java 中,Map 接口是 Java 集合框架的一部分,它存储键值对(key-value pairs)。Map 接口有许多常用的方法,用于添加、删除、获取元素&…...

基于springboot大学生兼职平台管理系统(完整源码+数据库)
一、项目简介 本项目是一套基于springboot大学生兼职平台管理系统 包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试,eclipse或者idea 确保可以运行! 该系统功能完善、界面美观、操作简单、功…...

C#学生信息管理系统
一、引言 学生信息管理系统是现代学校管理的重要组成部分,它能够有效地管理学生的基本信息、课程信息、成绩信息等,提高学校管理的效率和质量。本文将介绍如何使用SQL Server数据库和C#语言在.NET平台上开发一个学生信息管理系统的课程设计项目。 二、项…...

双机 Cartogtapher 建图文件配置
双机cartogtapher建图 最近在做硕士毕设的最后一个实验,其中涉及到多机建图,经过调研最终采用cartographer建图算法,其中配置多机建图的文件有些麻烦,特此博客以记录 非常感谢我的同门 ”叶少“ 山上的稻草人-CSDN博客的帮助&am…...

VMware提示 该虚拟机似乎正在使用中,如何解决?
VMware提示 该虚拟机似乎正在使用中,如何解决? 问题描述解决方法1.找到安装VMware的文件目录2.在VMware目录下.lck后缀的文件夹删除或重命名3.运行VMware 问题描述 该虚拟机似乎正在使用中。 如果该虚拟机未在使用,请按“获取所有权(T)”按钮获取它的所…...

阿里云短信服务业务
一、了解阿里云用户权限操作 1.注册账号、实名认证; 2.使用AccessKey 步骤一 点击头像,权限安全的AccessKey 步骤二 设置子用户AccessKey 步骤三 添加用户组和用户 步骤四 添加用户组记得绑定短信服务权限 步骤五 添加用户记得勾选openApi访问 添加…...
ElasticSearch的DSL查询
ElasticSearch的DSL查询 准备工作 创建测试方法,初始化测试结构。 import org.apache.http.HttpHost; import org.apache.lucene.search.TotalHits; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRespo…...
每天定时杀spark进程
##编写shell脚本 #!/bin/bash arr(“zhangsan” “lisi” “wangwu”) for i in “${arr[]}” do processps -ef|grep ${i}| grep -v "grep"| awk {print $2} kill -9 ${process} done ##每日定时杀手动启动的进程 0 19 * * * cd /kill_process && sh kil…...

win10 安装kubectl,配置config连接k8s集群
安装kubectl 按照官方文档安装:https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/ curl安装 (1)下载curl安装压缩包: curl for Windows (2)配置环境变量: 用户变量: Path变…...

Calico IPIP和BGP TOR的数据包走向
IPIP Mesh全网互联 文字描述 APOD eth0 10.7.75.132 -----> APOD 网关 -----> A宿主机 cali76174826315网卡 -----> Atunl0 10.7.75.128 封装 ----> Aeth0 10.120.181.20 -----> 通过网关 10.120.181.254 -----> 下一跳 BNODE eth0 10.120.179.8 解封装 --…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...

【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法
使用 ROS1-Noetic 和 mavros v1.20.1, 携带经纬度海拔的话题主要有三个: /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码,来分析他们的发布过程。发现前两个话题都对应了同一…...
深入解析 ReentrantLock:原理、公平锁与非公平锁的较量
ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...