websocket 配置
package com.sitech.cmap.comp.wsg.cntr.oracle.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @ClassName: WebSocketStompConfig
* @Description:
* @author: by_csd_hlj
* @date: 2019/1/25 13:34
* @version: 1.0
*/
@Configuration
public class WebSocketStompConfig{
@Bean
public ServerEndpointExporter serverEndpointExporter()
{
return new ServerEndpointExporter();
}
}
websocket
返回信息有两种形式
session.getAsyncRemote().sendText(json.toJSONString()); //异步返回
session.getBasicRemote().sendText(json.toJSONString()); //同步返回
常见异常:
The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method
具体选用哪个方法来返回信息,还需要看项目需求
返回信息,需要加上synchronized ,否则会出现 [TEXT_FULL_WRITING] 异常
package com.sitech.cmap.comp.wsg.cntr.oracle.websocket;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sitech.cmap.dao.simple.comp.mapper.OmCntrOraInsDetailMapper;
import com.sitech.cmap.dao.simple.comp.mapper.OmOracleInstanceMapper;
import com.sitech.cmap.dao.simple.comp.po.OmCntrOraInsDetail;
import com.sitech.cmap.dao.simple.comp.po.OmOracleInstance;
import com.sitech.cmap.fw.core.common.spring.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RestController;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
/**
* @ClassName:
* @Description:
* @author: by_csd_hlj
* @date:
* @version: 1.0
*/
@Slf4j
@RestController
@ServerEndpoint("/v1/cntr/oracle/websocket")
public class Websocket {
private Session session;
private String id;
ApplicationContext applicationContext = SpringContextHolder.getApplicationContext();
OmCntrOraInsDetailMapper detailMapper = (OmCntrOraInsDetailMapper) applicationContext.getBean("omCntrOraInsDetailMapper");
OmOracleInstanceMapper instanceMapper = (OmOracleInstanceMapper) applicationContext.getBean("omOracleInstanceMapper");
/**
* 建立连接
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
try {
log.info("websocket 已打开。");
} catch (Exception e){
log.error(e.getMessage(),e);
}
}
/**
* 连接关闭
*/
@OnClose
public void onClose()
{
//webSockets.remove(this);
}
@OnError
public void onError(Session session, Throwable error) {
log.info("服务端发生了错误"+error.getMessage());
//error.printStackTrace();
}
@OnMessage
public synchronized void onMessage(String message, Session session){
try {
//获取参数
JSONObject param = JSON.parseObject(message);
String instanceId = param.getString("id");
String type = param.getString("type");
String orderId = param.getString("orderId");
this.id = instanceId;
//获取远程主机用户、密码、ip
OmOracleInstance ins = instanceMapper.selectByPrimaryKey(instanceId);
OmCntrOraInsDetail d = new OmCntrOraInsDetail();
d.setInstanceId(instanceId);
OmCntrOraInsDetail de = detailMapper.selectOne(d);
//获取日志路径
String path ;
if("start".equals(type)){
path = de.getStartLog();
}else{
path = de.getStopLog();
}
//传参
JSONObject json = new JSONObject();
json.put("ip",ins.getIp());
json.put("username",de.getUsername());
json.put("password",de.getPassword());
json.put("comment",path);
json.put("id",instanceId);
json.put("orderId",orderId);
//websocket返回信息
WebsocketThread thread = new WebsocketThread(this,json);
new Thread(thread).start();
}catch (Exception e){
log.info("发生了错误了");
log.error(e.getMessage(),e);
}
}
//向前端发送消息
public void sendMessage(String line,String id){
try{
JSONObject json = new JSONObject();
json.put("id",id);
json.put("msg",line);
session.getAsyncRemote().sendText(json.toJSONString());
}catch (Exception e){
log.error(e.getMessage(),e);
}
}
}
使用线程执行ssh链接
package com.sitech.cmap.comp.wsg.cntr.oracle.websocket;
import com.alibaba.fastjson.JSONObject;
import com.sitech.cmap.dao.simple.comp.mapper.OmCntrExecHisMapper;
import com.sitech.cmap.dao.simple.comp.mapper.OmCntrOpRecordMapper;
import com.sitech.cmap.fw.core.common.spring.SpringContextHolder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
/**
* @ClassName:
* @Description:
* @author: by_csd_hlj
* @date:
* @version: 1.0
*/
@Slf4j
@Data
public class WebsocketThread implements Runnable{
private Websocket websocket;
private JSONObject json;
ApplicationContext applicationContext = SpringContextHolder.getApplicationContext();
OmCntrExecHisMapper hisMapper = (OmCntrExecHisMapper) applicationContext.getBean("omCntrExecHisMapper");
OmCntrOpRecordMapper recordMapper = (OmCntrOpRecordMapper) applicationContext.getBean("omCntrOpRecordMapper");
public WebsocketThread(Websocket websocket, JSONObject json){
this.websocket = websocket;
this.json = json;
}
@Override
public synchronized void run() {
try{
//ssh连接
SShAgent ssh = SShAgent.createJschSession("172.21.3.89", "comp", "comp");
ssh.execCommand("tail -f "+json.getString("comment")+" &",websocket);
}catch (Exception e){
log.error(e.getMessage(),e);
}
}
}
sshAgent
特别注意,多线程执行日志读取时,ssh链接是不关闭的,会一直占用cpu性能,所以加上判断,如果三分钟没有返回,就关闭ssh链接
package com.sitech.cmap.comp.wsg.cntr.oracle.websocket;
import com.jcraft.jsch.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
/**
* @ClassName:
* @Description:
* @author: by_csd_hlj
* @date:
* @version: 1.0
*/
@Slf4j
@Data
public class SShAgent {
private Session session;
private String host;
private int port;
private String user;
private String passwd;
private String md5Cmd;
public SShAgent(String host, int port, String user, String passwd) {
super();
this.host = host;
this.port = port;
this.user = user;
this.passwd = passwd;
}
public boolean expired() throws JSchException, IOException, InterruptedException{
Channel openChannel = session.openChannel("shell");
PipedInputStream pis = new PipedInputStream();//服务端输入管道
PipedOutputStream pos = new PipedOutputStream(pis);//指令写入管道
PipedInputStream reader = new PipedInputStream();//输出管道
PipedOutputStream out = new PipedOutputStream(reader);//服务器返回结果管道
openChannel.setInputStream(pis);
openChannel.setOutputStream(out);
openChannel.connect();
pos.write("echo 'success'\n".getBytes());
pos.flush();
Thread.sleep(1000);
String str = "";
byte [] buffer = new byte[1024];
while(reader.read(buffer)>-1){
str = new String(buffer);
break;
}
pos.close();
pis.close();
out.close();
reader.close();
openChannel.disconnect();
if(str.contains("Your password has expired")){
session.disconnect();
return true;
}else if(str.contains("success")){
return false;
}else{
return false;
}
}
public void initSession() throws Exception {
try {
if(session==null){
JSch jsch = new JSch();
session = jsch.getSession(user, host, port);
UserInfo ui = new MyUserInfo(passwd);
session.setUserInfo(ui);
session.setTimeout(1800000);
session.setServerAliveInterval(1800000);
session.connect();
}
} catch (Exception e) {
closeSession();
throw new Exception("主机密码不正确:"+host+"@"+user+"!");
}
}
public void closeSession(){
if(session!=null){
session.disconnect();
}
}
public static SShAgent createJschSession(String ip,String username,String passwd) throws Exception{
SShAgent jsch = new SShAgent(ip,22,username,passwd);
jsch.initSession();
if(jsch.expired()){
throw new Exception(ip+"@"+username+"主机密码已过期");
}
return jsch;
}
public static void closeJschSession(SShAgent client){
if(client!=null){
client.closeSession();
log.info("关闭"+client.getHost()+"@"+client.getUser()+"SSH连接");
}
}
public void execCommand(String command,Websocket websocket,String id) throws Exception {
try {
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
channel.connect();
byte[] tmp = new byte[1024];
//进入循环时间大于3分钟,自动结束
long currtime = System.currentTimeMillis();
while (true) {
long endTime = System.currentTimeMillis();
if(endTime - currtime > 3*60*1000){
break;
}
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0){
break;
}
//有返回时,更新一下时间,
currtime = System.currentTimeMillis();
//进程休眠,防止返回消息数量过多导致websocket报错
Thread.sleep(2000);
websocket.sendMessage( new String(tmp, 0, i),id);
}
if (channel.isClosed()) {
if (in.available() > 0){
continue;
}
break;
}
}
channel.disconnect();
} catch (Exception e) {
Thread.sleep(1500);
websocket.sendMessage( "读取日志失败!",id);
log.error(e.getMessage(),e);
e.printStackTrace();
throw new Exception(getHost()+"@"+getUser()+",指令:"+command+"执行失败");
}
}
public class MyUserInfo implements UserInfo {
String passwd;
public MyUserInfo(String passwd) {
super();
this.passwd = passwd;
}
@Override
public String getPassphrase() {
return null;
}
@Override
public String getPassword() {
return passwd;
}
@Override
public boolean promptPassphrase(String message) {
return true;
}
@Override
public boolean promptPassword(String message) {
return true;
}
@Override
public boolean promptYesNo(String str) {
return true;
}
@Override
public void showMessage(String message) {
System.out.println(message);
}
}
}
js
html页面可以根据自己的需求写,只要注意js
cons.initWebsocket = function(){
if ("WebSocket" in window)
{
webSocket = new WebSocket("ws://127.0.0.1:10090/cntr-service/v1/cntr/oracle/websocket");
//连通之后的回调事件
webSocket.onopen = function()
{
console.log("已经连通了websocket");
};
//接收后台服务端的消息
webSocket.onmessage = function (evt)
{
console.log("数据已接收!");
var json = JSON.parse(evt.data);
var textarea = $("div[data-id='"+json.id+"']").find("textarea");
var str = textarea.val()+json.msg;
textarea.text(str);
textarea .scrollTop( textarea[0].scrollHeight);
};
//连接关闭的回调事件
webSocket.onclose = function()
{
console.log("连接已关闭...");
};
}
else{
// 浏览器不支持 WebSocket
pluspop.alert("您的浏览器不支持 WebSocket!");
}
}
cons.sendMessage = function(param){
var ids = param.ids;
for(var i in ids){
var message = {
id:ids[i],
type:param.type,
orderId:param.orderId
};
webSocket.send(JSON.stringify(message));
}
}