当前位置: 首页 > news >正文

springboot websocket 持续打印 pod 日志

springboot 整合 websocket 和 连接 k8s 集群的方式参考历史 Java 专栏文章

  1. 修改前端页面
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Java后端WebSocket的Tomcat实现</title><script type="text/javascript"></script>
</head><body>
<div></div>
Welcome<br/>
<div id="message"></div>
</body>
<script type="text/javascript">var websocket = null;//判断当前浏览器是否支持WebSocketif('WebSocket' in window) {//改成你的地址// websocket = new WebSocket("ws://localhost:8080/api/websocket/100");websocket = new WebSocket("ws://localhost:8080/api/websocket/pod/mm-httpbin-b549bdf48-l2fjp/container/mm-httpbin");} else {alert('当前浏览器 Not support websocket')}//连接发生错误的回调方法websocket.onerror = function() {setMessageInnerHTML("WebSocket连接发生错误");};//连接成功建立的回调方法websocket.onopen = function() {setMessageInnerHTML("WebSocket连接成功");}var U01data, Uidata, Usdata//接收到消息的回调方法websocket.onmessage = function(event) {console.log(event.data);setMessageInnerHTML(event.data);}//连接关闭的回调方法websocket.onclose = function() {setMessageInnerHTML("WebSocket连接关闭");}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function() {closeWebSocket();}//将消息显示在网页上function setMessageInnerHTML(innerHTML) {document.getElementById('message').innerHTML += innerHTML + '<br/>';}//关闭WebSocket连接function closeWebSocket() {websocket.close();}//发送消息function send() {var message = document.getElementById('text').value;websocket.send('{"msg":"' + message + '"}');setMessageInnerHTML(message + "&#13;");}
</script></html>
  1. 修改 websocket 类

命名空间测试写死了,需要可以调整

package com.vazquez.k8sclient.websocket;import com.vazquez.k8sclient.util.K8sClient;
import io.kubernetes.client.PodLogs;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.util.Config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.*;/*** @Description* @Author vazquez* @DATE 2024/4/10 8:50*/@Slf4j
@Service
@ServerEndpoint("/api/websocket/pod/{podName}/container/{containerName}")
public class PodLogWebsocket {//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。private static int onlineCount = 0;//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。private static CopyOnWriteArraySet<PodLogWebsocket> podLogWebsocket = new CopyOnWriteArraySet<PodLogWebsocket>();//与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;private static final ExecutorService executorService = Executors.newCachedThreadPool();private static final int RECENT_LOG_LINE = 1000;private boolean flag = true;/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("podName") String podName, @PathParam("containerName") String containerName) {this.session = session;podLogWebsocket.add(this);     //加入set中addOnlineCount();           //在线数加1getPodLog(podName, containerName);try {sendMessage("conn_success");log.info("当前在线人数为:" + getOnlineCount());} catch (IOException e) {log.error("websocket IO Exception");}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {podLogWebsocket.remove(this);  //从set中删除subOnlineCount();           //在线数减1//断开连接情况下,更新主板占用情况为释放//这里写你 释放的时候,要处理的业务log.info("有一连接关闭!当前在线人数为" + getOnlineCount());}/*** 收到客户端消息后调用的方法* @ Param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session) {//群发消息for (PodLogWebsocket item : podLogWebsocket) {try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();}}}/*** @ Param session* @ Param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("发生错误");error.printStackTrace();}/*** 实现服务器主动推送*/public void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {PodLogWebsocket.onlineCount++;}public static synchronized void subOnlineCount() {PodLogWebsocket.onlineCount--;}public static CopyOnWriteArraySet<PodLogWebsocket> getWebSocketSet() {return podLogWebsocket;}private void getPodLog(String podName, String containerName) {executorService.execute(() -> {BufferedReader reader = null;InputStream is = null;try {//使用单例创建ApiClientString token = "ey...cJ29w";String url = "https://10.xxx6:6443";ApiClient client = Config.fromToken(url, token, false);Configuration.setDefaultApiClient(client);PodLogs logs = new PodLogs(client);//按时间过滤stream流会有堵塞延迟is = logs.streamNamespacedPodLog("default", podName, containerName, null, RECENT_LOG_LINE, false);reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));String line;//若ws连接已经断开,则不再读取数据输出到前端while (this.flag && this.session != null && this.session.isOpen()) {while ((line = readLineWithTimeout(podName, reader)) != null) {if (this.flag) {System.out.println(line);sendMessage(line);} else {break;}}}} catch (Exception e) {log.error("get container log Exception", e);} finally {try {if (reader != null) {reader.close();}} catch (IOException e) {log.error("reader IOException:{}", e.getMessage());}try {if (is != null) {is.close();}} catch (IOException e) {log.error("InputStreamReader IOException:{}", e.getMessage());}}});}/*** 读取一行bufferedReader数据并返回 堵塞超时返回null** @param bufferedReader* @return*/private static String readLineWithTimeout(String podName, BufferedReader bufferedReader) {Future<String> future;try {future = executorService.submit(() -> {try {return bufferedReader.readLine();} catch (IOException e) {return null;}});String message = future.get(10, TimeUnit.SECONDS);//当pod重启或删除时 由于连接会断开 future会直接返回数据为null 此时会不断请求 业务暂时添加10s休眠if (message == null) {Thread.sleep(10 * 1000);}return message;} catch (TimeoutException e) {// Timeout occurredlog.error(podName + " readLineWithTimeout " + Thread.currentThread().getId(), e);return null;} catch (InterruptedException | ExecutionException e) {e.printStackTrace();return null;}}
}
  1. 效果展示

在这里插入图片描述

相关文章:

springboot websocket 持续打印 pod 日志

springboot 整合 websocket 和 连接 k8s 集群的方式参考历史 Java 专栏文章 修改前端页面 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>Java后端WebSocket的Tomcat实现</title><script type"text/javasc…...

C代码编译过程与进程内存分布

C代码编译过程 在这篇文章中&#xff0c;我们将探讨C语言代码的编译流程以及进程在运行时的内存布局。编译过程通常包括几个关键步骤&#xff1a;预处理、编译、汇编和链接。 预处理阶段主要是处理源代码文件中的宏定义、头文件包含和条件编译指令。在此阶段&#xff0c;编译…...

Windows 部署ChatGLM3大语言模型

一、环境要求 硬件 内存&#xff1a;> 16GB 显存: > 13GB&#xff08;4080 16GB&#xff09; 硬盘&#xff1a;60G 软件 python 版本推荐3.10 - 3.11 transformers 库版本推荐为 4.36.2 torch 推荐使用 2.0 及以上的版本&#xff0c;以获得最佳的推理性能 二、部…...

JS相关八股之什么是事件循环

在JavaScript中&#xff0c;“事件循环”&#xff08;Event Loop&#xff09;是一个非常重要的概念&#xff0c;它是指JavaScript引擎如何在单线程中处理异步操作的机制。单线程意味着在任意时刻&#xff0c;JavaScript代码只能执行一个任务。 一.事件循环的工作流程大致如下&…...

SpringCloud集成Skywalking链路追踪和日志收集

1. 下载Agents https://archive.apache.org/dist/skywalking/java-agent/9.0.0/apache-skywalking-java-agent-9.0.0.tgz 2. 上传到服务器解压 在Spring Cloud项目中&#xff0c;每部署一个服务时&#xff0c;就拷贝一份skywalking的agent文件到该服务器上并解压。不管是部署…...

HTTP 域名和主机是一回事吗?有了主机和域名,如何建站?

域名不等于主机名&#xff0c;例如baidu.com是一个权威域的域名&#xff0c;但是根本没有一个主机的名字叫做baidu.com,但是dns.baidu.com就是一个主机名&#xff0c;它就是负责baidu.com的服务器的主机名&#xff0c;www.baidu.com也是一个主机名,它是百度web服务器的主机名。…...

运营干货:四个技巧掌握爆款选题方法

在运营工作中&#xff0c;选题是一项至关重要的工作&#xff0c;选对了一个热门话题&#xff0c;就能吸引大量用户的关注和互动&#xff0c;从而取得更好的运营成果。 今天&#xff0c;就给大家分享四个爆款选题方法&#xff0c;让大家的运营更上一层楼&#xff01; 第一种&a…...

柯桥商务口语之怎么样说英语更加礼貌?十个礼貌用语get起来!

当你在国外需要帮助的时候&#xff0c;这些礼貌用语真的是能够帮到你的哦 1.Would/Could you help me? 你可帮助我吗&#xff1f; 相信有些人想请求帮助的时候&#xff0c;一开口就用Can you&#xff0c;这个用在朋友或者熟人上面当然是没有问题的&#xff0c;但是如果是向…...

嵌入式工程师如何摸鱼?

有老铁问我&#xff0c;做嵌入式开发要加班吗&#xff1f; 也不知道搞什么鬼&#xff0c;现在的年轻人对加班这么抵触。 我刚做开发那会&#xff0c;啥也不懂&#xff0c;每天基本都要加班到晚上7-9点不等&#xff0c;我并不抵触加班&#xff0c;因为早早回家&#xff0c;也没什…...

C++语言题库(一)—— 基本知识类

目录 1. Hello World! 2. 据说一个人的标准体重应该是其身高&#xff08;单位&#xff1a;厘米&#xff09;减去100、再乘以0.9所得到的公斤数。已知市斤的数值是公斤数值的两倍。现给定某人身高&#xff0c;请你计算其标准体重应该是多少&#xff1f; 3. 给定一个华氏温度F…...

gemini1.5 API调用

https://ai.google.dev/pricing?hlzh-cn 查询可用的model https://generativelanguage.googleapis.com/v1beta/models?keyxxx 使用postman调用 https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?keyxxx https://ai.google…...

C++从入门到精通——const与取地址重载

const与取地址重载 前言一、const正常用法const成员函数问题const对象可以调用非const成员函数吗非const对象可以调用const成员函数吗const成员函数内可以调用其它的非const成员函数吗非const成员函数内可以调用其它的const成员函数吗总结 二、取地址及const取地址操作符重载概…...

手写spring IOC底层源码来模拟spring如何利用多级缓存解决循环依赖的问题

在文章开始之前&#xff0c;先来看一张spring IOC加载过程的脑图吧 Spring IOC的加载过程 首先,当我们去new了一个applicationContext,它底层呢就会把我们配置的bean进行扫描,然后创建成一个一个的beanDefinition放在我们的beanDefinitionMap中,此时就有了一切创造bean的原料信…...

C++11 Thead线程和线程池

参考资料&#xff1a; 2、5.lock_guard 与 std::unique_lock-陈子青的编程学习课堂 (seestudy.cn) 3、C11 多线程编程-小白零基础到手撕线程池_哔哩哔哩_bilibili 一、 C11 Thead线程库的基本使用 # include <thread> std::thread t(function_name, args...); // 线…...

Windows版Apache 2.4.59解压直用(免安装-绿色-项目打包直接使用)

windows下Apache分类 Apache分为 安装版和解压版 安装版: 安装方便&#xff0c;下一步------下一步就OK了&#xff0c;但重装系统更换环境又要重新来一遍&#xff0c;会特别麻烦 解压版&#xff08;推荐&#xff09;&#xff1a; 这种方式&#xff08;项目打包特别方便&#x…...

刀具表面上的微结构

刀具表面微结构通常指在刀具表面对特定功能设计的微观纹理&#xff0c;这些纹理可以是沟槽、凹坑、凸起或任何其他形式的微观图案。这些微结构的设计和应用是为了改善刀具的切削性能&#xff0c;减少切削力和切削温度&#xff0c;提高切削效率和精度&#xff0c;同时降低切削液…...

css3实现微信扫码登陆动画

在做微信扫码登陆时&#xff0c;出现一个背景光图上下扫码动画&#xff0c;用css3图片实现。 实现原理&#xff1a; 1.准备一个渐变的背景.png图 2.css动画帧实现动画 看效果&#xff1a; css代码&#xff1a; #wx-scan{position: absolute;top:0px;left: 50%;z-index: 3;ma…...

vue3 导入excel数据

所需包 "xlsx": "^0.18.5"页面导入包 import * as XLSX from xlsx; import {genFileId, UploadProps, UploadRawFile,ElTable } from element-plus;页面 <el-upload accept".xlsx" :on-change"changeExcel" :on-exceed"ha…...

C# linq 根据多字段动态Group by

实现类&#xff1a; public static class LinqHepler {/// <summary>/// 根据单个字段动态Group/// </summary>/// <typeparam name"T"></typeparam>/// <param name"source"></param>/// <param name"prop…...

C语言学习/复习22----阶段测评编程题

一、阶段测评练习 题1&#xff1a; 题2&#xff1a;...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...