前段时间做一个hadoop+Spark的性能监控页面时,需要一个web控制台远程登陆到master节点上去,后来发现这方面资料太少,于是自己参照着零散的东西修修改改,终于做出了一个简单的web shell,记录一下以免时间长了忘记。大概像这个样子的:
这样就可以在网页上直接访问linux服务器了,初衷是用来远程关闭正在运行的spark任务的,做发现出来之后一般的linux命令都能执行。
首先讲一下后台实现:
1.建立ssh连接,并定义一些流用于收发命令。
2.其次定义是一个接收命令和返回结果的方法,因为linux每次返回一行,所以我这里存入List
package com.java.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
public class SSHLinux
{
private String hostname = "11.11.11.15";
private int port = 22;
private String username = "root";
private String password = "123456";
private Connection conn;
private Session sess;
private BufferedReader stdout;
private BufferedReader stderr;
private PrintWriter pw;
public SSHLinux()
{
//使用默认值
long t1 = System.currentTimeMillis();
initSession();
long t2 = System.currentTimeMillis();
System.out.println("远程登陆linux,连接耗时:"+(t2-t1)/1000.0+"s");
}
public SSHLinux(String hostname, int port, String username, String password)
{
this.hostname = hostname;
this.port = port;
this.username = username;
this.password = password;
long t1 = System.currentTimeMillis();
initSession();
long t2 = System.currentTimeMillis();
System.out.println("远程登陆linux,连接耗时:"+(t2-t1)/1000.0+"s");
}
//初始化连接并建立虚拟终端
public void initSession()
{
try
{
conn = new Connection(hostname,port);
conn.connect();
boolean isAuthenticated = conn.authenticateWithPassword(username, password);
if (isAuthenticated == false)
throw new IOException("Authentication failed.");
sess = conn.openSession();
sess.requestDumbPTY();//建立虚拟终端
sess.startShell();//打开一个Shell
stdout = new BufferedReader(new InputStreamReader(new StreamGobbler(sess.getStdout()), StandardCharsets.UTF_8));
stderr = new BufferedReader(new InputStreamReader(new StreamGobbler(sess.getStderr()), StandardCharsets.UTF_8));
pw = new PrintWriter(sess.getStdin(),true);// 准备输入命令
}
catch (IOException e)
{
System.out.println("------建立ssh linux连接错误------");
}
}
public void close()
{
try
{
stdout.close();
stderr.close();
pw.close();
sess.close();
conn.close();
}
catch (Exception e)
{
System.out.println("------关闭ssh连接错误------");
}
}
public List execute(String strcmd)
{
System.out.println("在execute()中接收到命令:"+ strcmd );
List rstList = new ArrayList();
try
{
if(strcmd.equals("exit"))
{
pw.println(strcmd);// 输入待执行命令
close();
rstList.add("用户退出,关闭连接!!!");
return rstList;
}
pw.println(strcmd);// 输入待执行命令
while (true)
{
String line = stdout.readLine();
if (line == null || line.trim().endsWith("#"))
break;
System.out.println(line);
rstList.add(line);
}
}
catch (IOException e)
{
System.out.println("------连接已经关闭或命令出错------");
}
return rstList;
}
public static void main(String[] args)
{
SSHLinux ssh = new SSHLinux("192.168.0.160",22,"root","123456");
List list = ssh.execute("pwd");
System.out.println("list----: "+list);
//ssh.execute("exit");
}
}
3.页面后台调用前面初始化方法和执行命令的方法,这个方法叫getLinux(),主要是和前台页面交互。我在这个方法命名为linux,即@RequestMapping("/linux"),前台就可以通过linux.do识别了。
首先创建弹出框并初始化:
/*
* 创建按钮弹出框
*/
@RequestMapping("/addWindow")
public ModelAndView goNewAddServer(HttpServletRequest req)
{
//ssh连接linux---------从xml配置文件中读取参数
ApplicationContext ct = new ClassPathXmlApplicationContext("applicationContext.xml");
Permission per = (Permission) ct.getBean("linux");
String hostname = per.getIp();
int port = per.getPort();
String username = per.getUsername();
String password = per.getPassword();
System.out.println("ssh连接linux---------Permission="+per.toString());
try {
ssh = new SSHLinux(hostname,port,username,password);
//或者将账号写死
//ssh = new SSHLinux("192.168.1.12",22,"root","123456");
} catch (Exception e) {
e.printStackTrace();
}
return new ModelAndView("/admin/cloudcompute/box");///对应web console页面
}
其次执行命令,主要是和前台页面交互:
/*
* 在linux web shell中执行命令
*/
@RequestMapping("/linux")
@ResponseBody
private void getLinux(HttpServletRequest request, HttpServletResponse response)throws Exception{
System.out.println("\n-----------------Linux Shell-----------------\n");
String cmd = request.getParameter("code");
System.out.println("执行命令: "+cmd);
if(ssh!=null)
{
List list = new ArrayList();
if(cmd.length()==0)
{
System.out.println("输入为空");
//list.add("输入为空");
}
else if(cmd!="" || cmd.length()!=0 || cmd!=null)
{
list = ssh.execute(cmd);
}
System.out.println("返回结果list----: "+list);
String result = ""; //{'data:','
for(String str : list)
{
result += str.trim()+"
";
}
//result +="'}";
ResponseUtil.write(response, result);
//JsonBinder buildNormalBinder = JsonBinder.buildNormalBinder();
//return buildNormalBinder.toJson(result);
}
else
{
System.out.println("没有连接,不执行命令");
//return "";
}
}//
4.接下来看页面,页面很简单:主要是定义列表来接收命令,因为发送的命令占一行,回来的结果为一行或多行,所以
列表很合适。
控制台
这是原来的页面中增加的一个模态框,通过按钮触发。当然也可以新建一个页面专门来做控制台,代码都一样,如图:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
命令行窗口
- ----------------------命令Demo----------------------------
- 关闭进程方式1:
- ps -aux | grep test.jar //根据提交的jar包名字来查找进程,再kill -9 [pid]杀掉进程
- 关闭进程方式2:
- ps -aux | grep test.jar | kill -9 `awk '{print $2}'` //查找进程id并立即杀掉
- 关闭进程方式3:
- kill -9 $(cat my.pid) //前提:提交任务时获取进程号: spark-submit --class demo.WordCount test.jar & echo $!>my.pid
- ----------------------开始操作----------------------------