refactor(websocket): 重构 WebSocket 服务端
- 优化 CsWsClientManager 类,改用 clientMap 存储客户端连接 - 重构 CsWsMessage 类,将 bizType 改为 bizCode - 优化 CsWsServer 类,增加任务线程管理功能 - 重构 PipelineInstanceService 接口,修改 queryLastPipelineDetailWs 方法参数 - 优化 PipelineInstanceServiceImpl 中的查询逻辑 - 更新前端 WebSocket 连接逻辑,采用新的 sid 格式
This commit is contained in:
@@ -11,7 +11,7 @@ export default {
|
|||||||
* @param confirmCallBack 确认回调
|
* @param confirmCallBack 确认回调
|
||||||
* @param cancelCallBack 取消回调
|
* @param cancelCallBack 取消回调
|
||||||
*/
|
*/
|
||||||
FullConfirm(questionText, msgType, confirmCallBack, cancelCallBack) {
|
FullConfirm: function(questionText, msgType, confirmCallBack, cancelCallBack) {
|
||||||
let currentType = 'info'
|
let currentType = 'info'
|
||||||
if (msgType === 's') {
|
if (msgType === 's') {
|
||||||
currentType = 'success'
|
currentType = 'success'
|
||||||
@@ -38,7 +38,7 @@ export default {
|
|||||||
* @param confirmCallBack 确认回调
|
* @param confirmCallBack 确认回调
|
||||||
* @param cancelCallBack 取消回调
|
* @param cancelCallBack 取消回调
|
||||||
*/
|
*/
|
||||||
DeleteConfirm(confirmCallBack, cancelCallBack) {
|
DeleteConfirm: function(confirmCallBack, cancelCallBack) {
|
||||||
this.FullConfirm('确定删除本条数据吗?', 'w', confirmCallBack, cancelCallBack)
|
this.FullConfirm('确定删除本条数据吗?', 'w', confirmCallBack, cancelCallBack)
|
||||||
}, /**
|
}, /**
|
||||||
* 数据删除确认对话框(自定义提示内容)
|
* 数据删除确认对话框(自定义提示内容)
|
||||||
@@ -46,7 +46,7 @@ export default {
|
|||||||
* @param confirmCallBack 确认回调
|
* @param confirmCallBack 确认回调
|
||||||
* @param cancelCallBack 取消回调
|
* @param cancelCallBack 取消回调
|
||||||
*/
|
*/
|
||||||
DeleteMessageConfirm(message, confirmCallBack, cancelCallBack) {
|
DeleteMessageConfirm: function(message, confirmCallBack, cancelCallBack) {
|
||||||
this.FullConfirm(message, 'w', confirmCallBack, cancelCallBack)
|
this.FullConfirm(message, 'w', confirmCallBack, cancelCallBack)
|
||||||
}, /**
|
}, /**
|
||||||
* 数据输入对话框
|
* 数据输入对话框
|
||||||
@@ -56,7 +56,7 @@ export default {
|
|||||||
* @param confirmCallBack 校验通过回调
|
* @param confirmCallBack 校验通过回调
|
||||||
* @param cancelCallBack 取消回调
|
* @param cancelCallBack 取消回调
|
||||||
*/
|
*/
|
||||||
TextInputConfirm(title, patternText, errorMsg, confirmCallBack, cancelCallBack) {
|
TextInputConfirm: function(title, patternText, errorMsg, confirmCallBack, cancelCallBack) {
|
||||||
Vue.prototype.$prompt('', title, {
|
Vue.prototype.$prompt('', title, {
|
||||||
confirmButtonText: '确定', cancelButtonText: '取消', inputPattern: patternText, inputErrorMessage: errorMsg
|
confirmButtonText: '确定', cancelButtonText: '取消', inputPattern: patternText, inputErrorMessage: errorMsg
|
||||||
}).then(({ value }) => {
|
}).then(({ value }) => {
|
||||||
@@ -73,7 +73,7 @@ export default {
|
|||||||
* @param title 标题
|
* @param title 标题
|
||||||
* @param content 内容
|
* @param content 内容
|
||||||
*/
|
*/
|
||||||
ShowConfirm(title, content) {
|
ShowConfirm: function(title, content) {
|
||||||
Vue.prototype.$alert(content, title, {
|
Vue.prototype.$alert(content, title, {
|
||||||
confirmButtonText: '关闭'
|
confirmButtonText: '关闭'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,22 +3,22 @@ import Vue from 'vue'
|
|||||||
* 消息提示
|
* 消息提示
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
Info(content) {
|
Info: function(content) {
|
||||||
Vue.prototype.$message({
|
Vue.prototype.$message({
|
||||||
message: content, type: 'info'
|
message: content, type: 'info'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Success(content) {
|
Success: function(content) {
|
||||||
Vue.prototype.$message({
|
Vue.prototype.$message({
|
||||||
message: content, type: 'success'
|
message: content, type: 'success'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Warning(content) {
|
Warning: function(content) {
|
||||||
Vue.prototype.$message({
|
Vue.prototype.$message({
|
||||||
message: content, type: 'warning'
|
message: content, type: 'warning'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Error(content) {
|
Error: function(content) {
|
||||||
Vue.prototype.$message({
|
Vue.prototype.$message({
|
||||||
message: content, type: 'error'
|
message: content, type: 'error'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ import Vue from 'vue'
|
|||||||
* 通知
|
* 通知
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
Info(content) {
|
Info: function(content) {
|
||||||
Vue.prototype.$notify({ message: content, type: 'info' })
|
Vue.prototype.$notify({ message: content, type: 'info' })
|
||||||
}, Success(content) {
|
}, Success: function(content) {
|
||||||
Vue.prototype.$notify({ message: content, type: 'success' })
|
Vue.prototype.$notify({ message: content, type: 'success' })
|
||||||
}, SuccessDuration(content, durationTime) {
|
}, SuccessDuration: function(content, durationTime) {
|
||||||
Vue.prototype.$notify({ message: content, type: 'success', duration: durationTime })
|
Vue.prototype.$notify({ message: content, type: 'success', duration: durationTime })
|
||||||
}, Warning(content) {
|
}, Warning: function(content) {
|
||||||
Vue.prototype.$notify({ message: content, type: 'warning' })
|
Vue.prototype.$notify({ message: content, type: 'warning' })
|
||||||
}, Error(content) {
|
}, Error: function(content) {
|
||||||
Vue.prototype.$notify({ message: content, type: 'error' })
|
Vue.prototype.$notify({ message: content, type: 'error' })
|
||||||
}, ErrorDuration(content, durationTime) {
|
}, ErrorDuration: function(content, durationTime) {
|
||||||
Vue.prototype.$notify({ message: content, type: 'error', duration: durationTime })
|
Vue.prototype.$notify({ message: content, type: 'error', duration: durationTime })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ import { queryLastPipelineDetail, queryLastPipelineDetailWs, startPipeline } fro
|
|||||||
import { CountArraysObjectByPropKey, FormatDateTimeStr } from '@/utils/CsUtil'
|
import { CountArraysObjectByPropKey, FormatDateTimeStr } from '@/utils/CsUtil'
|
||||||
import CsMessage from '@/utils/elementui/CsMessage'
|
import CsMessage from '@/utils/elementui/CsMessage'
|
||||||
import CsWsClient from '@/utils/CsWsClient'
|
import CsWsClient from '@/utils/CsWsClient'
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CutePipelineNodeDemo',
|
name: 'CutePipelineNodeDemo',
|
||||||
@@ -472,6 +473,11 @@ export default {
|
|||||||
// }
|
// }
|
||||||
// this.connectWebSocketServer(pipelineInstanceId)
|
// this.connectWebSocketServer(pipelineInstanceId)
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'user'
|
||||||
|
])
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
refreshData() {
|
refreshData() {
|
||||||
// 成功
|
// 成功
|
||||||
@@ -509,7 +515,8 @@ export default {
|
|||||||
*/
|
*/
|
||||||
connectWebSocketServer(instanceId) {
|
connectWebSocketServer(instanceId) {
|
||||||
const that = this
|
const that = this
|
||||||
const sid = 'instanceId_' + instanceId
|
// {username}_{bizCode}_{contextParams}
|
||||||
|
const sid = `${that.user.username}_FetchPipelineLastDetail_${instanceId}`
|
||||||
that.dynamicWsClient = new CsWsClient(sid)
|
that.dynamicWsClient = new CsWsClient(sid)
|
||||||
that.dynamicWsClient.connect(that.handleWebSocketError, that.handleWebSocketMessage)
|
that.dynamicWsClient.connect(that.handleWebSocketError, that.handleWebSocketMessage)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -533,11 +540,17 @@ export default {
|
|||||||
// 判断流水线是否结束
|
// 判断流水线是否结束
|
||||||
const successCount = CountArraysObjectByPropKey(that.dynamicInstance.nodes, 'status', 'success')
|
const successCount = CountArraysObjectByPropKey(that.dynamicInstance.nodes, 'status', 'success')
|
||||||
if (that.dynamicTemplate && that.dynamicTemplate.length === successCount) {
|
if (that.dynamicTemplate && that.dynamicTemplate.length === successCount) {
|
||||||
|
// 所有节点执行成功
|
||||||
if (that.dynamicWsClient) {
|
if (that.dynamicWsClient) {
|
||||||
that.dynamicWsClient.close()
|
that.dynamicWsClient.close()
|
||||||
}
|
}
|
||||||
that.dynamicStartupStatus = that.dynamicStartupStatusMap.start.code
|
that.dynamicStartupStatus = that.dynamicStartupStatusMap.start.code
|
||||||
} else {
|
} else {
|
||||||
|
const failCount = CountArraysObjectByPropKey(that.dynamicInstance.nodes, 'status', 'fail')
|
||||||
|
// 存在失败的节点
|
||||||
|
if (that.dynamicWsClient && failCount > 0) {
|
||||||
|
that.dynamicWsClient.close()
|
||||||
|
}
|
||||||
that.dynamicStartupStatus = that.dynamicStartupStatusMap.restart.code
|
that.dynamicStartupStatus = that.dynamicStartupStatusMap.restart.code
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -594,8 +607,9 @@ export default {
|
|||||||
CsMessage.Success('流水线启动成功')
|
CsMessage.Success('流水线启动成功')
|
||||||
// await that.fetchLastDetail(result.instanceId)
|
// await that.fetchLastDetail(result.instanceId)
|
||||||
sessionStorage.setItem('pipelineInstanceId', result.instanceId)
|
sessionStorage.setItem('pipelineInstanceId', result.instanceId)
|
||||||
this.connectWebSocketServer(result.instanceId)
|
that.connectWebSocketServer(result.instanceId)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error('error', e)
|
||||||
that.dynamicStartButtonLoading = false
|
that.dynamicStartButtonLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import LineChart from './dashboard/LineChart'
|
|||||||
import RadarChart from '@/components/Echarts/RadarChart'
|
import RadarChart from '@/components/Echarts/RadarChart'
|
||||||
import PieChart from '@/components/Echarts/PieChart'
|
import PieChart from '@/components/Echarts/PieChart'
|
||||||
import BarChart from '@/components/Echarts/BarChart'
|
import BarChart from '@/components/Echarts/BarChart'
|
||||||
import CsMessage from '@/utils/elementui/CsMessage'
|
|
||||||
|
|
||||||
const lineChartData = {
|
const lineChartData = {
|
||||||
newVisitis: {
|
newVisitis: {
|
||||||
|
|||||||
+22
-9
@@ -1,39 +1,52 @@
|
|||||||
package cn.odboy.framework.websocket.context;
|
package cn.odboy.framework.websocket.context;
|
||||||
|
|
||||||
import javax.websocket.CloseReason;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket客户端管理
|
||||||
|
*
|
||||||
|
* @author odboy
|
||||||
|
* @date 2025-07-24
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
public class CsWsClientManager {
|
public class CsWsClientManager {
|
||||||
/**
|
/**
|
||||||
* concurrent包的线程安全Map, 用来存放每个客户端对应的MyWebSocket对象。(分布式必出问题)
|
* concurrent包的线程安全Map, 用来存放每个客户端对应的MyWebSocket对象。(分布式必出问题)
|
||||||
*/
|
*/
|
||||||
private static final Map<String, CsWsServer> client = new ConcurrentHashMap<>();
|
private static final Map<String, CsWsServer> clientMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public static void addClient(String sid, CsWsServer csWsServer) {
|
public static void addClient(String sid, CsWsServer csWsServer) {
|
||||||
// 如果存在就先删除一个, 防止重复推送消息
|
// 如果存在就先删除一个, 防止重复推送消息
|
||||||
removeClient(sid);
|
removeClient(sid);
|
||||||
client.put(sid, csWsServer);
|
clientMap.put(sid, csWsServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeClient(String sid) {
|
public static void removeClient(String sid) {
|
||||||
try {
|
CsWsServer wsServer = clientMap.get(sid);
|
||||||
CsWsServer wsServer = client.get(sid);
|
|
||||||
if (wsServer != null) {
|
if (wsServer != null) {
|
||||||
|
try {
|
||||||
|
// log.info("关闭session, sid={}", sid);
|
||||||
wsServer.getSession().close();
|
wsServer.getSession().close();
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
client.remove(sid);
|
// log.info("停止任务, sid={}", sid);
|
||||||
|
wsServer.stopTask(sid);
|
||||||
|
// log.info("移除客户端, sid={}", sid);
|
||||||
|
clientMap.remove(sid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Collection<CsWsServer> getAllClient() {
|
public static Collection<CsWsServer> getAllClient() {
|
||||||
return client.values();
|
return clientMap.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CsWsServer getClientBySid(String sid) {
|
public static CsWsServer getClientBySid(String sid) {
|
||||||
return client.get(sid);
|
return clientMap.get(sid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -7,11 +7,11 @@ import lombok.Setter;
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class CsWsMessage extends CsObject {
|
public class CsWsMessage extends CsObject {
|
||||||
private String bizType;
|
private String bizCode;
|
||||||
private String data;
|
private String data;
|
||||||
|
|
||||||
public CsWsMessage(String bizType, String data) {
|
public CsWsMessage(String bizCode, String data) {
|
||||||
this.bizType = bizType;
|
this.bizCode = bizCode;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-37
@@ -9,7 +9,9 @@ import javax.websocket.*;
|
|||||||
import javax.websocket.server.PathParam;
|
import javax.websocket.server.PathParam;
|
||||||
import javax.websocket.server.ServerEndpoint;
|
import javax.websocket.server.ServerEndpoint;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -17,6 +19,7 @@ import java.util.Objects;
|
|||||||
@ServerEndpoint("/websocket/{sid}")
|
@ServerEndpoint("/websocket/{sid}")
|
||||||
@Getter
|
@Getter
|
||||||
public class CsWsServer {
|
public class CsWsServer {
|
||||||
|
private static final Map<String, Thread> taskThreadMap = new ConcurrentHashMap<>();
|
||||||
/**
|
/**
|
||||||
* 与某个客户端的连接会话, 需要通过它来给客户端发送数据
|
* 与某个客户端的连接会话, 需要通过它来给客户端发送数据
|
||||||
*/
|
*/
|
||||||
@@ -32,10 +35,24 @@ public class CsWsServer {
|
|||||||
@OnOpen
|
@OnOpen
|
||||||
public void onOpen(Session session, @PathParam("sid") String sid) {
|
public void onOpen(Session session, @PathParam("sid") String sid) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
// {username}#{bizCode}#{contextParams}
|
||||||
this.sid = sid;
|
this.sid = sid;
|
||||||
CsWsClientManager.addClient(sid, this);
|
CsWsClientManager.addClient(sid, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收到客户端消息后调用的方法
|
||||||
|
*
|
||||||
|
* @param message 客户端发送过来的消息
|
||||||
|
*/
|
||||||
|
@OnMessage
|
||||||
|
public void onMessage(String message, Session session) {
|
||||||
|
CsWsMessage wsMessage = JSON.parseObject(message, CsWsMessage.class);
|
||||||
|
String bizCode = wsMessage.getBizCode();
|
||||||
|
Object data = wsMessage.getData();
|
||||||
|
log.info("收到来 sid={} 的信息: message={}, bizCode={}, data={}", sid, message, bizCode, JSON.toJSONString(data));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 连接关闭调用的方法
|
* 连接关闭调用的方法
|
||||||
*/
|
*/
|
||||||
@@ -48,46 +65,13 @@ public class CsWsServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 收到客户端消息后调用的方法
|
|
||||||
*
|
|
||||||
* @param message 客户端发送过来的消息
|
|
||||||
*/
|
|
||||||
@OnMessage
|
|
||||||
public void onMessage(String message, Session session) {
|
|
||||||
CsWsMessage wsMessage = JSON.parseObject(message, CsWsMessage.class);
|
|
||||||
String bizType = wsMessage.getBizType();
|
|
||||||
Object data = wsMessage.getData();
|
|
||||||
log.info("收到来 sid={} 的信息: message={}, bizType={}, data={}", sid, message, bizType, JSON.toJSONString(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群发消息
|
|
||||||
*
|
|
||||||
* @param message /
|
|
||||||
*/
|
|
||||||
private void sendToAll(String message) {
|
|
||||||
for (CsWsServer item : CsWsClientManager.getAllClient()) {
|
|
||||||
try {
|
|
||||||
item.innerSendMessage(message);
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("发送消息给 sid={} 失败", item.sid, e);
|
|
||||||
CsWsClientManager.removeClient(item.sid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnError
|
@OnError
|
||||||
public void onError(Session session, Throwable error) {
|
public void onError(Session session, Throwable error) {
|
||||||
log.error("WebSocket sid={} 发生错误", this.sid, error);
|
// log.error("WebSocket sid={} 发生错误", this.sid, error);
|
||||||
CsWsServer client = CsWsClientManager.getClientBySid(this.sid);
|
|
||||||
if (client != null) {
|
|
||||||
try {
|
try {
|
||||||
client.getSession().close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
CsWsClientManager.removeClient(this.sid);
|
CsWsClientManager.removeClient(this.sid);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +83,7 @@ public class CsWsServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 群发自定义消息
|
* 精准推送消息
|
||||||
*/
|
*/
|
||||||
public void sendMessage(CsWsMessage message, @PathParam("sid") String sid) throws IOException {
|
public void sendMessage(CsWsMessage message, @PathParam("sid") String sid) throws IOException {
|
||||||
String body = JSON.toJSONString(message);
|
String body = JSON.toJSONString(message);
|
||||||
@@ -117,6 +101,20 @@ public class CsWsServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群发消息
|
||||||
|
*/
|
||||||
|
public void sendToAll(String message) {
|
||||||
|
for (CsWsServer item : CsWsClientManager.getAllClient()) {
|
||||||
|
try {
|
||||||
|
item.innerSendMessage(message);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("发送消息给 sid={} 失败", item.sid, e);
|
||||||
|
CsWsClientManager.removeClient(item.sid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
@@ -133,4 +131,34 @@ public class CsWsServer {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(session, sid);
|
return Objects.hash(session, sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Thread getTaskThread(String sid) {
|
||||||
|
Thread thread = taskThreadMap.get(sid);
|
||||||
|
if (thread == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (thread.isInterrupted()) {
|
||||||
|
taskThreadMap.remove(sid);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restartTask(Runnable runnable) {
|
||||||
|
stopTask(this.sid);
|
||||||
|
Thread thread = new Thread(runnable);
|
||||||
|
thread.start();
|
||||||
|
taskThreadMap.put(this.sid, thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopTask(String sid) {
|
||||||
|
Thread thread = getTaskThread(this.sid);
|
||||||
|
if (thread != null) {
|
||||||
|
try {
|
||||||
|
thread.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -3,11 +3,13 @@ package cn.odboy.devops.dal.model;
|
|||||||
import cn.odboy.base.CsObject;
|
import cn.odboy.base.CsObject;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class DevOpsQueryLastPipelineDetailArgs extends CsObject {
|
public class DevOpsQueryLastPipelineDetailArgs extends CsObject {
|
||||||
@NotNull(message = "流水线实例Id必填")
|
@NotBlank(message = "流水线实例Id必填")
|
||||||
private String instanceId;
|
private String instanceId;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -103,6 +103,7 @@ public class PipelineJobBean implements InterruptableJob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 流水线正式启动
|
||||||
pipelineNodeJobManage.startJob(pipelineInstanceTb, pipelineNodeTemplateVo);
|
pipelineNodeJobManage.startJob(pipelineInstanceTb, pipelineNodeTemplateVo);
|
||||||
pipelineInstanceTb.setCurrentNode(pipelineNodeTemplateVo.getCode());
|
pipelineInstanceTb.setCurrentNode(pipelineNodeTemplateVo.getCode());
|
||||||
pipelineInstanceTb.setCurrentNodeStatus(PipelineStatusEnum.RUNNING.getCode());
|
pipelineInstanceTb.setCurrentNodeStatus(PipelineStatusEnum.RUNNING.getCode());
|
||||||
@@ -120,15 +121,16 @@ public class PipelineJobBean implements InterruptableJob {
|
|||||||
pipelineInstanceMapper.updateById(pipelineInstanceTb);
|
pipelineInstanceMapper.updateById(pipelineInstanceTb);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ThreadUtil.safeSleep(5000);
|
// 检查实例节点状态
|
||||||
PipelineInstanceNodeTb pipelineInstanceNode = pipelineInstanceNodeService.getPipelineInstanceNodeByArgs(instanceId, pipelineNodeTemplateVo.getCode());
|
ThreadUtil.safeSleep(3000);
|
||||||
if (PipelineStatusEnum.SUCCESS.getCode().equals(pipelineInstanceNode.getCurrentNodeStatus())) {
|
PipelineInstanceNodeTb runningInstanceNode = pipelineInstanceNodeService.getPipelineInstanceNodeByArgs(instanceId, pipelineNodeTemplateVo.getCode());
|
||||||
|
if (PipelineStatusEnum.SUCCESS.getCode().equals(runningInstanceNode.getCurrentNodeStatus())) {
|
||||||
pipelineStatusEnum = PipelineStatusEnum.SUCCESS;
|
pipelineStatusEnum = PipelineStatusEnum.SUCCESS;
|
||||||
pipelineInstanceTb.setCurrentNodeStatus(pipelineStatusEnum.getCode());
|
pipelineInstanceTb.setCurrentNodeStatus(pipelineStatusEnum.getCode());
|
||||||
pipelineInstanceMapper.updateById(pipelineInstanceTb);
|
pipelineInstanceMapper.updateById(pipelineInstanceTb);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (PipelineStatusEnum.FAIL.getCode().equals(pipelineInstanceNode.getCurrentNodeStatus())) {
|
if (PipelineStatusEnum.FAIL.getCode().equals(runningInstanceNode.getCurrentNodeStatus())) {
|
||||||
pipelineStatusEnum = PipelineStatusEnum.FAIL;
|
pipelineStatusEnum = PipelineStatusEnum.FAIL;
|
||||||
pipelineInstanceTb.setCurrentNodeStatus(pipelineStatusEnum.getCode());
|
pipelineInstanceTb.setCurrentNodeStatus(pipelineStatusEnum.getCode());
|
||||||
pipelineInstanceMapper.updateById(pipelineInstanceTb);
|
pipelineInstanceMapper.updateById(pipelineInstanceTb);
|
||||||
|
|||||||
+1
-1
@@ -19,5 +19,5 @@ public interface PipelineInstanceService {
|
|||||||
|
|
||||||
PipelineInstanceVo queryLastPipelineDetail(String instanceId);
|
PipelineInstanceVo queryLastPipelineDetail(String instanceId);
|
||||||
|
|
||||||
void queryLastPipelineDetailWs(String instanceId);
|
void queryLastPipelineDetailWs(String sid);
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-22
@@ -17,6 +17,7 @@ import cn.odboy.devops.service.PipelineInstanceNodeDetailService;
|
|||||||
import cn.odboy.devops.service.PipelineInstanceNodeService;
|
import cn.odboy.devops.service.PipelineInstanceNodeService;
|
||||||
import cn.odboy.devops.service.PipelineInstanceService;
|
import cn.odboy.devops.service.PipelineInstanceService;
|
||||||
import cn.odboy.framework.exception.BadRequestException;
|
import cn.odboy.framework.exception.BadRequestException;
|
||||||
|
import cn.odboy.framework.websocket.context.CsWsClientManager;
|
||||||
import cn.odboy.framework.websocket.context.CsWsMessage;
|
import cn.odboy.framework.websocket.context.CsWsMessage;
|
||||||
import cn.odboy.framework.websocket.context.CsWsServer;
|
import cn.odboy.framework.websocket.context.CsWsServer;
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
@@ -24,11 +25,8 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@@ -39,8 +37,6 @@ public class PipelineInstanceServiceImpl implements PipelineInstanceService {
|
|||||||
private final PipelineInstanceDAO pipelineInstanceDAO;
|
private final PipelineInstanceDAO pipelineInstanceDAO;
|
||||||
private final PipelineInstanceNodeService pipelineInstanceNodeService;
|
private final PipelineInstanceNodeService pipelineInstanceNodeService;
|
||||||
private final PipelineInstanceNodeDetailService pipelineInstanceNodeDetailService;
|
private final PipelineInstanceNodeDetailService pipelineInstanceNodeDetailService;
|
||||||
private final CsWsServer csWsServer;
|
|
||||||
private volatile Map<String, Thread> runningThreadMap = new HashMap<>();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StartPipelineResultVo startPipeline(PipelineInstanceTb pipelineInstanceTb) {
|
public StartPipelineResultVo startPipeline(PipelineInstanceTb pipelineInstanceTb) {
|
||||||
@@ -109,32 +105,29 @@ public class PipelineInstanceServiceImpl implements PipelineInstanceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queryLastPipelineDetailWs(String instanceId) {
|
public void queryLastPipelineDetailWs(String sid) {
|
||||||
Thread thread = runningThreadMap.get(instanceId);
|
// sid {username}_{bizCode}_{contextParams}
|
||||||
if (thread != null && !thread.isInterrupted()) {
|
String[] sids = sid.split("_");
|
||||||
try {
|
if (sids.length != 3) {
|
||||||
thread.stop();
|
throw new BadRequestException("sid格式异常");
|
||||||
} catch (Exception e) {
|
|
||||||
// ignore
|
|
||||||
}
|
}
|
||||||
}
|
CsWsServer wsServer = CsWsClientManager.getClientBySid(sid);
|
||||||
thread = new Thread(() -> {
|
wsServer.restartTask(() -> {
|
||||||
|
// String username = sids[0];
|
||||||
|
String bizCode = sids[1];
|
||||||
|
String instanceId = sids[2];
|
||||||
boolean loop = true;
|
boolean loop = true;
|
||||||
final String currentSid = instanceId;
|
|
||||||
final String realInstanceId = currentSid.replace("instanceId_", "");
|
|
||||||
while (loop) {
|
while (loop) {
|
||||||
ThreadUtil.safeSleep(1000);
|
ThreadUtil.safeSleep(1000);
|
||||||
try {
|
try {
|
||||||
PipelineInstanceVo pipelineInstanceVo = queryLastPipelineDetail(realInstanceId);
|
PipelineInstanceVo pipelineInstanceVo = queryLastPipelineDetail(instanceId);
|
||||||
CsWsMessage message = new CsWsMessage("FetchPipelineLastDetail", JSON.toJSONString(pipelineInstanceVo));
|
CsWsMessage message = new CsWsMessage(bizCode, JSON.toJSONString(pipelineInstanceVo));
|
||||||
csWsServer.sendMessage(message, currentSid);
|
wsServer.sendMessage(message, sid);
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
log.error("推送流水线最新数据失败", e);
|
log.error("推送流水线最新数据失败", e);
|
||||||
loop = false;
|
loop = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
thread.start();
|
|
||||||
runningThreadMap.put(instanceId, thread);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ mybatis-plus:
|
|||||||
# 是否开启自动驼峰命名规则(camel case)映射, 即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
|
# 是否开启自动驼峰命名规则(camel case)映射, 即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
|
||||||
map-underscore-to-camel-case: true
|
map-underscore-to-camel-case: true
|
||||||
# Sql日志
|
# Sql日志
|
||||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
# 启动时是否检查 MyBatis XML 文件的存在, 默认不检查
|
# 启动时是否检查 MyBatis XML 文件的存在, 默认不检查
|
||||||
check-config-location: true
|
check-config-location: true
|
||||||
# MyBatis Mapper 所对应的 XML 文件位置
|
# MyBatis Mapper 所对应的 XML 文件位置
|
||||||
@@ -236,7 +236,7 @@ mongo-plus:
|
|||||||
username: cutejava
|
username: cutejava
|
||||||
password: 123456
|
password: 123456
|
||||||
authentication-database: cutejava
|
authentication-database: cutejava
|
||||||
log: true
|
log: false
|
||||||
pretty: false
|
pretty: false
|
||||||
configuration:
|
configuration:
|
||||||
field:
|
field:
|
||||||
|
|||||||
Reference in New Issue
Block a user