Spring Boot 集成 WebSocket 实例 | 前端持续打印远程日志文件更新内容(模拟 tail 命令)

news/2024/5/30 18:53:33/文章来源:https://blog.csdn.net/ssrc0604hx/article/details/136718655

这个是我在 CSDN 的第一百篇原则博文,留念😎

#1 需求说明

先说下项目结构,后端基于 Spring Boot 3,前端为 node.js 开发的控制台程序。现在希望能够在前端模拟 tail 命令,持续输出后端的日志文件。

#2 技术方案

#2.1 基于轮询(PASS)

这个方案实施较为简单,通过前端不断(定时)发起请求,并携带已读的内容坐标(position),询问后端日志文件是否有更新,判断依据为当前文件大小大于 position。若有变动,则读取更新的内容,回显在前端控制台。

此方案会产生非常多的请求,如果定时间隔设置不好,会有明显的延迟,故不采用。

#2.2 WebSocket 长连接

  1. 前端开启一个 WebSocket
  2. 后端监听到长连接后,启动文件变动检测线程
  3. 若文件发生变动,则读取更新内容,发送到前端

#3 实施

#3.1 后端改造

关于 Spring Boot 与 WebSocket 的集成,请转到:springboot集成websocket持久连接(权限过滤+拦截)

首先,我们定义一个监听文件变动并读取最新内容的工具类(借助于 common-io 包):

class FileTail(val path:Path, val handler: Consumer<String>, delay:Long=1000): FileAlterationListenerAdaptor() {private val watcher = FileSystems.getDefault().newWatchService()private val MODE = "r"private var reader = RandomAccessFile(path.toFile(), MODE)private var position= reader.length()// 使用 JDK 自带的 WatchService ,发现不能正常读取文件追加的内容private var monitor: FileAlterationMonitor = FileAlterationMonitor(delay)init {// 初始化监视器,只检测同名的文件FileAlterationObserver(path.parent.toFile()) { f: File -> f.name == path.name }.also { observer->observer.addListener(this)monitor.addObserver(observer)monitor.start()}}override fun onFileChange(file: File) {reader.seek(position)val bytes = mutableListOf<Byte>()val tmp = ByteArray(1024)var readSize: Intwhile ((reader.read(tmp).also { readSize = it }) != -1) {for (i in 0..< readSize){bytes.add(tmp[i])}}position += bytes.sizehandler.accept(String(bytes.toByteArray()))}fun stop() {reader.close()monitor.stop()}
}

再定义长连接的通信处理类:

@Component
class FileTailWsHandler : TextWebSocketHandler() {private val logger = LoggerFactory.getLogger(javaClass)companion object {val monitors = mutableMapOf<String, FileTail>()}override fun afterConnectionEstablished(session: WebSocketSession) {try{val textFile = Paths.get("logs/spring.log")// 加入队列monitors[session.id] = FileTail(textFile,{ text -> session.sendMessage(TextMessage(text)) })}catch (e:Exception){logger.error("处理客户端消息失败", e)session.sendMessage(TextMessage("服务器出错:${ExceptionUtils.getMessage(e)}"))session.close(CloseStatus.SERVER_ERROR)}}override fun afterConnectionClosed(session: WebSocketSession, status: CloseStatus) {logger.info("客户端(${session.id}${session.remoteAddress} 断开连接...")monitors.remove(session.id)?.stop()}
}

编写配置类,启用上述的组件:

@Component
class WsInterceptor : HandshakeInterceptor {private val logger = LoggerFactory.getLogger(javaClass)override fun beforeHandshake(request: ServerHttpRequest,response: ServerHttpResponse,wsHandler: WebSocketHandler,attributes: MutableMap<String, Any>): Boolean {if(logger.isDebugEnabled){logger.debug("WS 握手开始:${request.uri} 客户端=${request.remoteAddress}")request.headers.forEach { name, v -> logger.debug("[HEADER] $name = $v") }}//此处可以进行鉴权//写入属性值,方便在 handler 中获取attributes[F.PARAMS]    = request.headers.getFirst(F.PARAMS)?: EMPTY// 返回 true 才能建立连接return true}override fun afterHandshake(request: ServerHttpRequest,response: ServerHttpResponse,wsHandler: WebSocketHandler,exception: Exception?) {}
}@Configuration
@EnableWebSocket
class SocketConfig : WebSocketConfigurer {private val logger = LoggerFactory.getLogger(javaClass)@Resourcelateinit var interceptor: WsInterceptor@Resourcelateinit var fileTailHandler:FileTailWsHandleroverride fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) {registry.addHandler(fileTailHandler, "/ws/file-tail").addInterceptors(interceptor)}
}

#3.2 前端(node.js)

请先安装依赖:npm i -D ws

/*** 跟踪远程日志文件* @param {*} ps*/
const _tailRemoteFile = async ps=>{let url = remoteUrl("/ws/file-tail")let index = url.indexOf("://")let headers = {}headers.params = JSON.stringify(ps)const client = new WebSocket(`ws${url.substring(index)}`, { headers })client.on('open', ()=> console.debug(chalk.magenta(`与服务器连接成功 🤝`)))// client.on('close',()=> console.debug(chalk.magenta(`\n与服务器连接关闭 👋`)))client.on('error', e=> {console.debug(chalk.red(e))})client.on('message', /** @param {Buffer} buf */buf=>{let line = buf.toString()if(line.endsWith("\n") || line.endsWith("\r\n"))line = line.substring(0, line.length-2)console.debug(line)})
}

#3.3 看看效果

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

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

相关文章

腾讯云轻量服务器地域选择教程,2024最新地域选择攻略

腾讯云服务器地域怎么选择&#xff1f;不同地域之间有什么区别&#xff1f;腾讯云哪个地域好&#xff1f;地域选择遵循就近原则&#xff0c;访客距离地域越近网络延迟越低&#xff0c;速度越快。腾讯云百科txybk.com告诉大家关于地域的选择还有很多因素&#xff0c;地域节点选择…

Gitee 服务器

Git 服务器集成 1. 创建仓库 2. 远程仓库简易操作指令 # Git 全局设置&#xff0c;修改成自己的信息 git config --global user.name "Muko" git config --global user.email "txk0x7d2163.com" # 创建 git 仓库&#xff0c;基本操作指令和其他远程仓库一…

若依Vue3图片预览大图遮罩层和表格的border css层级冲突

样式层级出现问题&#xff0c;表格的层级高于图片的层级 1.解决方式一&#xff1a;设置此文件的该属性&#xff08;z-index&#xff09;为继承&#xff0c;则显示正常 .el-table .el-table__cell { z-index: inherit; } 2.解决方式二&#xff1a;将此属性设置为true(本人试了…

基础---nginx 启动不了,跟 Apache2 服务冲突

文章目录 查看 nginx 服务状态nginx 启动后 访问页面 127.0.0.1停止 nginx 服务&#xff0c;访问不了页面停止/启动 Apache2 服务&#xff0c;启动 Apache2 页面访问显示正确nginx 莫名启动不了卸载 Apache2 服务器 启动 nginx &#xff0c;但是总是不能实现反向代理&#xff0…

UE4案例记录

UE4案例记录&#xff08;制作3D角色显示在UI中&#xff09; 制作3D角色显示在UI中 转载自youtube视频 https://www.youtube.com/channel/UCC8f6SxKJElVvaRb7nF4Axg 新建项目 创建一个Actor 场景组件->摄像机组件->场景捕获组件2D&#xff0c;之后添加一个骨骼网格体…

sheng的学习笔记- AI-类别不平衡问题

目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 什么是类别不平衡问题 类别不平衡&#xff08;class-imbalance&#xff09;&#xff0c;也叫数据倾斜&#xff0c;数据不平衡&#xff0c;就是指分类任务中不同类别的训练样例数目差别很大的情况。 例如有998个反例&#xf…

Python数据分析-4

1.对于一组电影数据&#xff0c;呈现出rating,runtime的分布情况&#xff1a; #encodingutf-8 import pandas as pd import numpy as np from matplotlib import pyplot as plt file_path "./youtube_video_data/IMDB-Movie-Data.csv" df pd.read_csv(file_path) …

OceanBase原理之内存管理

第1章 前言 1.1 多租户管理简介 OceanBase数据库中&#xff0c;应用了单集群多租户的设计&#xff0c;使得一个集群内能够创建多个彼此独立的租户。在OceanBase数据库&#xff0c;租户成为了资源分配的单位&#xff0c;同时还是数据库对象管理和资源管理的基础。 在某种程度…

电脑远程桌面选项变成灰色没办法勾选怎么办?

有些人在使用Windows系统自带的远程桌面工具时&#xff0c;会发现系统属性远程桌面选项卡中勾选启用“允许远程连接到此计算机”。 导致此问题出现的原因主要是由于组策略或者注册表设置错误造成的。 修复远程桌面选项变灰的两种方法&#xff01; 方法一&#xff1a;设置本地组…

Android 学习之追踪应用的安装情况

先上结论&#xff0c;急用的话直接看结论 结论一、借助 API 读取安装信息&#xff0c;然后上报二、借助手动埋点&#xff0c;然后上报三、对比 前提过程 结论 一、借助 API 读取安装信息&#xff0c;然后上报 通过 PackageManager 的 API&#xff0c;我们可以得知自身应用安装…

qiankun:vite/webpack项目配置

相关博文&#xff1a; https://juejin.cn/post/7216536069285429285?searchId202403091501088BACFF113F980BA3B5F3 https://www.bilibili.com/video/BV12T411q7dq/?spm_id_from333.337.search-card.all.click qiankun结构&#xff1a; 主应用base&#xff1a;vue3historyv…

无需编程技能:Python爬虫与数据可视化毕业论文代写服务

引言 作为一名在软件技术领域深耕多年的专业人士,我不仅在软件开发和项目部署方面积累了丰富的实践经验,更以卓越的技术实力获得了🏅30项软件著作权证书的殊荣。这些成就不仅是对我的技术专长的肯定,也是对我的创新精神和专业承诺的认可。我的专业知识涵盖了从前端界面设…

vue.config.js 配置proxy代理

方案一&#xff1a; 配置文件 文件内容 # 开发环境 NODE_ENV development # base api VUE_APP_BASE_API /api # 开发环境&#xff0c;Url地址 VUE_APP_URL https://localhost:44367/api 三个文件分别是三个不同环境使用的&#xff0c;如线上&#xff0c;线上测试&#xff…

整数序列删除指定数字,其他数顺序不变

#include <stdio.h>//整数序列删除一个数&#xff0c;其他数顺序不变 int main() {int arr[50] {0};//int arr[50] {1,2,3,4,5};int n 0;int del 0;int i 0;int j 0;scanf("%d",&n);for(i 0;i < n;i)scanf("%d",&arr[i]);scanf(&q…

新生宿舍管理系统|基于springboot框架+ Mysql+Java+B/S架构的新生宿舍管理系统设计与实现(可运行源码+数据库+设计文档+部署说明)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 学生功能模块 管理员功能 系统功能设计 数据库E-R图设计 lunwen参考 摘要 研究目的 开发环境 项目部…

【网络安全】-数字证书

数字证书 数字证书是互联网通讯中用于标志通讯各方身份信息的一串数字或数据&#xff0c;它为网络应用提供了一种验证通信实体身份的方式。具体来说&#xff0c;数字证书是由权威的证书授权&#xff08;CA&#xff09;中心签发的&#xff0c;包含公开密钥拥有者信息以及公开密…

YOLOv9改进策略:注意力机制 | EMA:基于跨空间学习的高效多尺度注意力,效果优于ECA、CBAM、CA

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进内容&#xff1a;加入EMA注意力&#xff0c;一种基于跨空间学习的高效多尺度注意力&#xff0c;效果优于ECA、CBAM、CA等经典注意力。 yolov9-c-EMA summary: 970 layers, 51011154 parameters, 51011122 gradients, 238.9 GF…

Springboot整合Mybaits启动过程

Springboot整合Mybaits启动过程 1.前言2.MybatisAutoConfiguration3.SqlSessionFactoryBean3.1 XMLConfigBuilder.parse()3.1.1 XMLMapperBuilder.parse()3.1.1.1 XMLStatementBuilder.parse() 4.SqlSession4.1 Executor 1.前言 直接加载mybatis配置文件&#xff0c;然后创建S…

hex文件格式解析

本文框架 1. hex文件格式1.1 数据长度1.2 地址域1.3 数据类型1.4 数据域1.5 CRC校验域 本文对hex文件格式进行解析&#xff0c;介绍各部分组成及其含义&#xff0c;在此mark下&#xff0c;方便后续开发脚本对hex文件进行操作。 1. hex文件格式 Intel HEX文件是由一行行符合Int…

基于鹦鹉优化算法(Parrot optimizer,PO)的无人机三维路径规划(提供MATLAB代码)

一、无人机路径规划模型介绍 无人机三维路径规划是指在三维空间中为无人机规划一条合理的飞行路径&#xff0c;使其能够安全、高效地完成任务。路径规划是无人机自主飞行的关键技术之一&#xff0c;它可以通过算法和模型来确定无人机的航迹&#xff0c;以避开障碍物、优化飞行…