springboot 整合 websocket 和 连接 k8s 集群的方式参考历史 Java 专栏文章
- 修改前端页面
hetml
<!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;
//判断当前浏览器是否支持WebSocket
if('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 + " ");
}
</script>
</html>
- 修改 websocket 类
命名空间测试写死了,需要可以调整
java
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;
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("podName") String podName, @PathParam("containerName") String containerName) {
this.session = session;
podLogWebsocket.add(this); //加入set中
addOnlineCount(); //在线数加1
getPodLog(podName, containerName);
try {
sendMessage("conn_success");
log.info("当前在线人数为:" + getOnlineCount());
} catch (IOException e) {
log.error("websocket IO Exception");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
podLogWebsocket.remove(this); //从set中删除
subOnlineCount(); //在线数减1
//断开连接情况下,更新主板占用情况为释放
//这里写你 释放的时候,要处理的业务
log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @ Param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
//群发消息
for (PodLogWebsocket item : podLogWebsocket) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @ Param session
* @ Param error
*/
@OnError
public 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 {
//使用单例创建ApiClient
String 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 occurred
log.error(podName + " readLineWithTimeout " + Thread.currentThread().getId(), e);
return null;
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
}
- 效果展示