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

这个是我在 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.hqwc.cn/news/539697.html

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

相关文章

Ansible自动化运维Inventory与Ad-Hoc

前言 自动化运维是指利用自动化工具和技术来简化、自动化和优化IT基础设施的管理和运维过程&#xff0c;从而提高效率、降低成本&#xff0c;并减少人为错误。在当今复杂的IT环境中&#xff0c;自动化运维已经成为许多组织和企业提高生产力和保证系统稳定性的重要手段。Ansibl…

react native常用插件

react-native-async-storage/async-storage 说明&#xff1a;AsyncStorage 是一个在 react-native 中轻量存储的库&#xff1b;跟 localStorage 类似&#xff0c;API 也几乎一样&#xff1b;存储的时候需要将存储内容转成字符串存储。 react-navigation/material-bottom-tabs …

SwiftUI的textfile

SwiftUI的textfile 记录一下SwiftUI的textfile的基本使用方法 import SwiftUIstruct TextfileBootCamp: View {State var enterString ""State var enterList [String]()var body: some View {NavigationView(content: {VStack {/// 双向绑定输入的字符TextField(…

泽众云真机-机型支持ADB调试功能即将上线

最近云真机平台在线客服&#xff0c;收到很多咨询关于ADB调试功能&#xff0c;什么时候能更新&#xff1f;据小编所知&#xff0c;正在升级之中&#xff0c;有一块专门为了解决ADB调试功能提前准备&#xff0c;升级网络硬件设备&#xff0c;目前平台的功能已开发完成&#xff0…

使用docker-compose部署MySQL三主六从半同步集群(MMM架构)

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容部署MySQL三主六从半同步集群一主二从同步集群规划需要安装docker和docker-compose命令形式安装安装docker安装docker-compose 宝塔面板形式安装 部署node1节点的master1docker-compose.yaml文件my.cnf文件授权启动 部署no…

C# danbooru Stable Diffusion 提示词反推 Onnx Demo

目录 说明 效果 模型信息 项目 代码 下载 C# danbooru Stable Diffusion 提示词反推 Onnx Demo 说明 模型下载地址&#xff1a;https://huggingface.co/deepghs/ml-danbooru-onnx 效果 模型信息 Model Properties ------------------------- ----------------------…

OpenvSwitch VXLAN 隧道实验

OpenvSwitch VXLAN 隧道实验 最近在了解 openstack 网络&#xff0c;下面基于ubuntu虚拟机安装OpenvSwitch&#xff0c;测试vxlan的基本配置。 节点信息&#xff1a; 主机名IP地址OS网卡node1192.168.95.11Ubuntu 22.04ens33node2192.168.95.12Ubuntu 22.04ens33 网卡信息&…

2024/3/14打卡棋子(14届蓝桥杯)——差分

标准差分模板 差分——前缀和的逆运算&#xff08;一维二维&#xff09;-CSDN博客 题目 小蓝拥有 nn 大小的棋盘&#xff0c;一开始棋盘上全都是白子。 小蓝进行了 m 次操作&#xff0c;每次操作会将棋盘上某个范围内的所有棋子的颜色取反(也就是白色棋子变为黑色&#xff0…

【数据结构与算法】(13):交换排序之冒泡排序和快速排序

&#x1f921;博客主页&#xff1a;Code_文晓 &#x1f970;本文专栏&#xff1a;数据结构与算法 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多数据结构与算法点击专栏链接查看&…

StarRocks——滴滴的极速多维分析实践

背景 滴滴集团作为生活服务领域的头部企业&#xff0c;其中橙心优选经过一年多的数据体系建设&#xff0c;逐渐将一部分需要实时交互查询&#xff0c;即席查询的多维数据分析需求由ClickHouse迁移到了StarRocks中&#xff0c;接下来以StarRocks实现的漏斗分析为例介绍StarRocks…

MapReduce解析:从定义到核心思想,编程规范与序列化解读

目录 一、 MapReduce1.1 MapReduce定义1.2 MapReduce优缺点1.2.1 优点1.2.2 缺点 1.3 MapReduce核心思想1.4 MapReduce进程1.5 常用数据序列化类型1.6 MapReduce编程规范1.6.1Mapper阶段1.6.2 Reduce阶段1.6.3 Driver阶段 1.7 WordCount案例实操1.7.1 本地测试1.7.2 提交到集群…

论文阅读——RingMo

RingMo: A Remote Sensing Foundation Model With Masked Image Modeling 与自然场景相比&#xff0c;RS图像存在以下困难。 1&#xff09;分辨率和方位范围大&#xff1a;受遥感传感器的影响&#xff0c;图像具有多种空间分辨率。此外&#xff0c;与自然图像的实例通常由于重…