依赖与配置
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
websocket 服务
package com.ybs.websocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@ServerEndpoint("/socketserver/{taskId}")
@Component
public class WebSocketServer {
private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
private Session session;
private String taskId = "";
@OnOpen
public void onOpen(Session session, @PathParam("taskId") String taskId) {
this.session = session;
this.taskId = taskId;
if (webSocketMap.containsKey(taskId)) {
webSocketMap.remove(taskId);
webSocketMap.put(taskId, this);
} else {
webSocketMap.put(taskId, this);
log.info(webSocketMap.toString());
}
try {
sendMessage("连接成功: " + taskId);
log.info("建立连接:{}", taskId);
} catch (IOException e) {
log.error("socket>>" + taskId + ",网络异常!!!!!!");
}
}
@OnClose
public void onClose() {
webSocketMap.remove(taskId);
log.info("断开连接:{}", taskId);
}
@OnMessage
public void onMessage(String message, Session session) throws IOException {
log.info("socket>>>:" + taskId + ",报文:" + message);
}
@OnError
public void onError(Session session, Throwable error) {
log.error("用户错误:" + this.taskId + ",原因:" + error.getMessage());
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
synchronized (this.session) {
this.session.getBasicRemote().sendText(message);
}
}
public ConcurrentHashMap<String, WebSocketServer> getWebSocketMap() {
return webSocketMap;
}
}
连接远程服务工具类
package com.ybs.utils;
import com.jcraft.jsch.*;
import com.ybs.websocket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
@Component
@Slf4j
public class ExecuteShellUtil {
public WebSocketServer webSocketServer;
private static final int TIME_OUT = 10 * 60 * 1000;
private static final String DONOT_INIT_ERROR_MSG = "please invoke init(...) first!";
private Session session;
private Channel channel;
private ChannelExec channelExec;
private ExecuteShellUtil() {
}
public static ExecuteShellUtil getInstance() {
return new ExecuteShellUtil();
}
public static ExecuteShellUtil getInstance(WebSocketServer webSocketServer) {
ExecuteShellUtil executeShellUtil = new ExecuteShellUtil();
executeShellUtil.webSocketServer = webSocketServer;
return executeShellUtil;
}
public void init(String ip, Integer port, String username, String password) throws JSchException {
JSch jsch = new JSch();
jsch.getSession(username, ip, port);
session = jsch.getSession(username, ip, port);
session.setPassword(password);
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
session.setConfig(sshConfig);
session.connect(60 * 1000);
session.setTimeout(TIME_OUT);
log.info("Session connected!");
channel = session.openChannel("exec");
channelExec = (ChannelExec) channel;
}
public String execCmd(String command) throws Exception {
if (session == null || channel == null || channelExec == null) {
throw new Exception(DONOT_INIT_ERROR_MSG);
}
log.info("execCmd command - > {}", command);
channelExec.setCommand(command);
channel.setInputStream(null);
channelExec.setErrStream(System.err);
channel.connect();
StringBuilder sb = new StringBuilder(16);
try (InputStream in = channelExec.getInputStream();
InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)) {
String buffer;
while ((buffer = reader.readLine()) != null) {
System.out.println(buffer);
sb.append("\n").append(buffer);
}
close();
log.info("execCmd result - > {}", sb);
return sb.toString();
}
}
public void execCmdAndSendWebSocket(String taskId, String command) throws Exception {
if (session == null || channel == null || channelExec == null) {
throw new Exception(DONOT_INIT_ERROR_MSG);
}
log.info("execCmd command - > {}", command);
channelExec.setCommand(command);
channel.setInputStream(null);
channelExec.setErrStream(System.err);
channel.connect();
try (InputStream in = channelExec.getInputStream();
InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)) {
String buffer;
while ((buffer = reader.readLine()) != null) {
sendMessage(taskId, buffer);
}
close();
}
}
private void close() {
if (channelExec != null && channelExec.isConnected()) {
channelExec.disconnect();
}
if (channel != null && channel.isConnected()) {
channel.disconnect();
}
if (session != null && session.isConnected()) {
session.disconnect();
}
}
public static String executeShell(String ip, String username, String password, String cmd) throws Exception {
ExecuteShellUtil instance = ExecuteShellUtil.getInstance();
instance.init(ip, 22, username, password);
return instance.execCmd(cmd);
}
public static void executeShellAndSendWebSocket(String taskId, WebSocketServer webSocketServer, String ip, String username, String password, String cmd) throws Exception {
ExecuteShellUtil instance = ExecuteShellUtil.getInstance(webSocketServer);
instance.init(ip, 22, username, password);
instance.execCmdAndSendWebSocket(taskId, cmd);
}
public void sendMessage(String taskId, String msg) {
try {
if (webSocketServer != null) {
webSocketServer.sendMessage(msg);
} else {
log.warn("客户端已退出");
}
} catch (IOException e) {
log.error("向客户端发送消息时出现异常,异常原因:{}", e.getMessage(), e);
}
}
public static void main(String[] args) throws Exception {
ExecuteShellUtil instance = ExecuteShellUtil.getInstance();
instance.init("192.168.2.156", 22, "root", "mima");
String result = instance.execCmd("cd /home/paulson/ybs; ls");
instance.close();
System.out.println(result);
}
}
Service
package com.ybs.service;
import com.ybs.utils.ExecuteShellUtil;
import com.ybs.websocket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
@Service
@Slf4j
public class DevOpsService {
@Autowired
private WebSocketServer webSocketServer;
@Async
public void executeShellAndSendWebSocket(String taskId) throws Exception {
ConcurrentHashMap<String, WebSocketServer> webSocketMap = webSocketServer.getWebSocketMap();
WebSocketServer webSocketServer = webSocketMap.get(taskId);
log.info("webSocketServer is {}, taskId is {}", webSocketServer, taskId);
if (webSocketServer == null) {
throw new Exception("请建立长链接!");
}
String cmd = "cd /home/paulson/ybs; " +
"tail -fn300 marklog.log";
ExecuteShellUtil.executeShellAndSendWebSocket(taskId, webSocketServer, "192.168.2.156", "root", "mima", cmd);
}
public void sendMessage(String taskId, String msg) {
try {
ConcurrentHashMap<String, WebSocketServer> map = webSocketServer.getWebSocketMap();
WebSocketServer server = map.get(taskId);
if (server != null) {
server.sendMessage(msg);
} else {
log.warn("客户端已退出");
}
} catch (IOException e) {
log.error("向客户端发送消息时出现异常,异常原因:{}", e.getMessage(), e);
}
}
}
@GetMapping("/devops")
public String devops(@RequestParam String taskId) throws Exception {
devOpsService.executeShellAndSendWebSocket(taskId);
return "操作成功";
}
前端
<template>
<div>
<h1>
DevOps
</h1>
<el-button type="primary"
@click="excute"
style="color: red">部署日志</el-button>
<el-button type="primary"
@click="excute"
style="color: red">服务日志</el-button>
<el-button type="primary"
@click="initWebSocket"
style="color: red">更新</el-button>
<span type="primary"
style="color: yellow; margin-left:200px">执行结果:{{excute_result}}</span>
<div style="height: 500px; overflow-y: scroll; background: #000000; color: #aaa; padding: 20px; text-align:left;margin:50px">
<div v-html="res"
id="logContent"></div>
</div>
</div>
</template>
<script>
export default {
name: 'test',
data () {
return {
excute_result: '等待中', // 启动中 成功 失败
websock: null,
serviceName: 'mercury',
res: "<div style='color: #18d035;font-size: 14px'>连接成功,等待执行....</div>"
}
},
created () {
this.initWebSocket();
},
destroyed () {
this.websock.close() //离开路由之后断开websocket连接
},
methods: {
excute () {
console.log("devops")
this.excute_result = '启动中'
this.$axios.get('/devops', {
params: {
'taskId': this.serviceName
}
}).then(res => {
console.log(res.data)
})
},
initWebSocket () { //初始化weosocket
const wsuri = "ws://127.0.0.1:8888/socketserver/" + this.serviceName;
this.websock = new WebSocket(wsuri);
this.websock.onmessage = this.websocketonmessage;
this.websock.onopen = this.websocketonopen;
this.websock.onerror = this.websocketonerror;
this.websock.onclose = this.websocketclose;
},
websocketonopen () { //连接建立之后执行send方法发送数据
let actions = { "test": "12345" };
this.websocketsend(JSON.stringify(actions));
},
websocketonerror () {//连接建立失败重连
this.initWebSocket();
},
websocketonmessage (e) { //数据接收
console.log('收到websoct数据:', e)
if (e.data.indexOf("Shutting down ExecutorService 'applicationTaskExecutor") != -1) {
this.excute_result = '启动失败'
}
if (e.data.indexOf("ERROR") != -1 || (e.data.indexOf("Shutting")) != -1 || (e.data.indexOf("Caused by")) != -1 || (e.data.indexOf(" was already in use")) != -1) {
this.res += "<div style='color: red;font-size: 14px'>" + e.data + "</div>"
} else if (e.data.indexOf("Tomcat started on port(s)") != -1) {
this.excute_result = '启动成功'
this.res += "<div style='color: blue;font-size: 14px'>" + e.data + "</div>"
} else {
this.res += "<div style='color: #18d035;font-size: 14px'>" + e.data + "</div>"
}
},
websocketsend (Data) {//数据发送
this.websock.send(Data);
},
websocketclose (e) { //关闭
console.log('断开连接', e);
},
},
}
</script>
<style scoped>
.el-button--primary {
color: white;
background-color: black;
border-color: #1d1d1f;
}
</style>
结果展示