Java通过shell脚本监控重启服务

简介最近运维人员提出需求,增加一个运维页面, 查询当前的业务进程信息包括:进程名称、启动命令、启动时间、运行时间等,可以通过页面点击重启按钮,可以重启后端的一系列系统进程 。
思路JAVA程序获取linux进程信息可以通过shell脚本获取进程信息、通过读取proc文件系统获取进程信息 。但是为了系统的安全性、方便维护等角度出发,更多的是java通过shell获取和linux交互能力 。
java程序中要执行linux命令主要依赖2个类:Process和Runtime:
Process:
ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例, 该实例可用来控制进程并获得相关信息 。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、 检查进程的退出状态以及销毁(杀掉)进程的方法 。创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft windows 上的 Win16/DOS 进程,或者 shell 脚本 。创建的子进程没有自己的终端或控制台 。它的所有标准 io(即 stdin、stdout 和 stderr) 操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程 。父进程使用这些流来提供到子进程的输入和获得从子进程的输出 。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小, 如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁 。当没有 Process 对象的更多引用时,不是删掉子进程,而是继续异步执行子进程 。对于带有 Process 对象的 Java 进程,没有必要异步或并发执行由 Process 对象表示的进程 。

  • 特别需要注意的是:
【Java通过shell脚本监控重启服务】1,创建的子进程没有自己的终端控制台,所有标注操作都会通过三个流
(getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程(父进程可通过这些流判断子进程的执行情况)
2,因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,
则可能导致子进程阻塞,甚至产生死锁
特别需要注意:如果子进程中的输入流,输出流或错误流中的内容比较多,最好使用缓存(注意上面的情况2)
Runtime
每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接 。可以通过getRuntime方法获取当前运行时环境 。应用程序不能创建自己的Runtime类实例 。
Process exec(String command)在单独的进程中执行指定的字符串命令 。Process exec(String command, String[] envp)在指定环境的单独进程中执行指定的字符串命令 。Process exec(String command, String[] envp, File dir)在有指定环境和工作目录的独立进程中执行指定的字符串命令 。Process exec(String[] cmdarray)在单独的进程中执行指定命令和变量 。Process exec(String[] cmdarray, String[] envp)在指定环境的独立进程中执行指定命令和变量 。Process exec(String[] cmdarray, String[] envp, File dir)在指定环境和工作目录的独立进程中执行指定的命令和变量 。command:一条指定的系统命令 。envp:环境变量字符串数组,其中每个环境变量的设置格式为name=value;如果子进程应该继承当前进程的环境,则该参数为null 。dir:子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为null 。cmdarray:包含所调用命令及其参数的数组 。获取进程信息
  • 获取进程的shell字符串 ps aux | grep procName| grep -v grep
  • java 调用shell 获取进程信息
/*** 执行shell 获取进程列表信息* @param cmd* @return*/private List<ProcessBean> queryProcessByShellCmd(final String cmd) {List<ProcessBean> processBeanList = new ArrayList<ProcessBean>();Process process = null;BufferedReader bufferOutReader = null, buffErrReader = null;String command = StringUtils.trimToNull(cmd);if(StringUtils.isEmpty(command)) {return processBeanList;}try {process = Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", command});process.waitFor();bufferOutReader = new BufferedReader(new InputStreamReader(process.getInputStream()));String line = null;while((line = bufferOutReader.readLine()) != null) {//解析ps返回的进程信息,组装成ProcessBean对象ProcessBean processBean = parserProcessBean(line);if (processBean == null) {continue;}logger.info("=============>>> 查询进程返回信息:{},解析进程对象信息:{}", line, processBean);processBeanList.add(processBean);}bufferOutReader.close();bufferOutReader = null;buffErrReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));while((line = buffErrReader.readLine()) != null) {logger.info("=============>>> 读取错误管道流信息:{}", line);}buffErrReader.close();buffErrReader = null;process.destroy();process = null;} catch (IOException e) {logger.error("======>>执行Shell脚本失败, e:{}", e);} catch (InterruptedException e) {logger.error("======>>执行Shell脚本失败, e:{}", e);} finally {try {if(bufferOutReader != null) {bufferOutReader.close();}if(buffErrReader != null) {buffErrReader.close();}if(process != null) process.destroy();} catch (Exception e){logger.error("===========>> {}", e);}}return processBeanList;}


推荐阅读