基于 JSch 实现服务的自定义监控解决方案
Posted 小毕超
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于 JSch 实现服务的自定义监控解决方案相关的知识,希望对你有一定的参考价值。
一、基于 JSch 实现服务的自定义监控
JSch
是 SSH2
的一个纯 Java
实现。它允许你连接到一个 sshd
服务器,使用端口转发,X11
转发,文件传输等等。你可以将它的功能集成到你自己的 程序中。
既然可以通过 SSH
连接到服务器,那就可以执行一些 命令 ,例如我们要监控一个服务是否正在运行,或者服务有无僵死,可以通过查看服务进程是否存在,访问接口是否正常来判断,如果不正常,我们可以通过 JSch
连接到该服务器中,执行重启的脚本。
现在对于新的项目相信大家都已经放在 k8s
中部署了,在 k8s
中有完善的服务检测机制,可以实现服务宕机的重起,服务僵死重启等功能,那为什么博主还要写这篇博客呢,相信大家应该都遇到过一些比较老的项目吧,由于项目比较老不容易打包成镜像放在 k8s
中运行,但是这种项目还依然运行着一些比较重要的功能,对其可用性还是需要保障。或者公司中有项目并不是放在容器中运行的,一时也不想迁移,针对这种情况下就可以参考本篇文章的内容。
下面我们将实现一个简单的场景,服务器中运行着一个普通的 java
项目,我们需要保障其运行的可用性,如果出现宕机需要及时的进行启动。
这里博主就有两种实现方案了:
第一种是我们在另一台服务器中准备一个监控服务,该服务通过JSch
连接到服务器中,定时查看是否存在 java
项目进程,如果进程不存在则进行重启指令。同时也可以通过指令监控服务的内存、磁盘等使用大小。
第二种就是借助 zookeeper
的事件通知机制,第一种方式通过定时的方式,势必会有一定的时差。如果需要服务一宕机,监控服务立马可以检测到的话,可以借助 zookeeper
的事件通知机制,被检测的项目在运行的时候去 zookeeper
建立一个属于该服务的临时节点,如果服务宕机则会因为 session
连接终断,临时节点自动移除,此时监控该节点的 session
会立马收到通知。但这种方式需要被检测服务操作 zookeeper
有一定的侵入性,另外有可能因为网络的震荡,导致连接中断,因此在收到删除事件通知时,建议再去服务中查看下进程是否真的宕机。
以上两种方案都是简单的说了下思路,具体实施还是有很多注意点,本篇文章就基于第一种方式实现自定义监控。
环境准备:
首先准备一个 java 项目,这里为了方便我准备了一个 SpringBoot 项目,并运行在了服务器中:
下面开始监控服务的搭建:
二、监控服务搭建
首先新建一个 SpringBoot
项目,在 pom 中引入 JSch
的依赖:
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
创建一个 SSHVO
存放主机信息:
@Data
public class SSHVO
private String host;
private String userName;
private String password;
private Integer port;
下面便是本篇文章的主要代码, 通过 JSch
进行远程主机的连接获得连接 Session
,并通过 Session
执行命令:
@Slf4j
@Data
public class SSHUtil
private SSHVO sshVo;
private Session session;
public SSHUtil(SSHVO sshVo)
this.sshVo = sshVo;
public void connect() throws Exception
ValidResult validResult = ValidationUtil.fastFailValidate(sshVo);
if (validResult.isHasError())
throw new Exception(validResult.getErrMessage());
JSch jsch = new JSch();
session = jsch.getSession(sshVo.getUserName(), sshVo.getHost(), (sshVo.getPort() == null || sshVo.getPort() == 0) ? 22 : sshVo.getPort());
session.setPassword(sshVo.getPassword());
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setTimeout(3000);
session.connect();
log.info("SSH 连接成功 > ", sshVo.toString());
public boolean isConnect()
if (session == null)
return false;
return session.isConnected();
public String command(String command) throws Exception
if (session == null)
connect();
log.info("SSH 执行命令 > , ", sshVo.toString(), command);
Channel channel = null;
try
channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
channel.connect();
InputStream in = channel.getInputStream();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, Charset.forName("UTF-8"))))
StringBuilder stringBuffer = new StringBuilder();
String buf = "";
while ((buf = reader.readLine()) != null)
stringBuffer.append(buf);
log.info("SSH 执行命令 > ,返回 >> ", sshVo.toString(), stringBuffer.toString());
return stringBuffer.toString();
catch (Exception e)
throw e;
finally
if (channel != null)
channel.disconnect();
public boolean close()
if (session == null)
return false;
session.disconnect();
return true;
上面实现了对远程主机的连接和执行命令操作,如果每次都创建一个新的 Session
肯定会造成资源浪费,下面我们做一个对 Session
缓存的操作,这里使用了 guava
中的缓存 Api
,需要引入 guava
的依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
下面使用 Cache
缓存连接对象:
@Slf4j
@Service
public class SSHService
private Cache<String, SSHUtil> sshConnectCache = CacheBuilder.newBuilder()
.maximumSize(2000)
.expireAfterWrite(2, TimeUnit.DAYS).build();
/**
* 执行命令
*/
public String command(SSHVO sshVo, String command)
try
if (StringUtils.isEmpty(sshVo.getHost()))
throw new Exception("host is null !");
SSHUtil tool = sshConnectCache.getIfPresent(sshVo.getHost());
if (tool == null)
tool = new SSHUtil(sshVo);
tool.connect();
sshConnectCache.put(sshVo.getHost(), tool);
return tool.command(command);
catch (Exception e)
log.error("执行SSH 失败!", e);
return null;
下面创建一个 定时任务,定时获取进程 ID,如果ID不存在则进行启动:
@Slf4j
@Component
@EnableScheduling
public class SSHTask
@Autowired
SSHService sshService;
@Scheduled(cron = "0/5 * * * * *")
public void sshTask() throws InterruptedException
SSHVO sshvo = new SSHVO();
sshvo.setHost("192.168.40.170");
sshvo.setUserName("root");
sshvo.setPassword("bxc");
sshvo.setPort(22);
String command = "ps aux |grep java-server-0.0.1.jar |grep -v grep | awk 'print $2'";
// 执行命令
String resCommand = sshService.command(sshvo, command);
log.info("主机:,指令:,返回:", sshvo.getHost(), command, resCommand);
if (StringUtils.isEmpty(resCommand))
//重启
String startCommand = "nohup java -jar /opt/java/java-server-0.0.1.jar > /opt/java/nohup.out 2>&1 &";
String resstartCommand = sshService.command(sshvo, startCommand);
log.info("主机:,指令:,返回:", sshvo.getHost(), startCommand, resstartCommand);
上面每5秒进行检测一次,启动项目查看打印日志:
可以看到打印出服务的进程 ID ,下面我们手动去服务中将该进程杀掉:
下面再观看打印的日志:
可以看到服务已经自动启动起来了,进入主机中查看下进程:
服务已经成功被启动起来。
以上是关于基于 JSch 实现服务的自定义监控解决方案的主要内容,如果未能解决你的问题,请参考以下文章
目标定位基于matlab多机EKF+时差和频差无源定位含Matlab源码 2057期