JAVA執行遠端服務器的腳本


JAVA執行遠端服務器的腳本

問題描述

工作中遇到這樣一個問題,我們的應用為了實現高可用會采取雙機部署,拓撲圖大致如下:
系統大致拓撲圖
這種方案可以簡單的保證高可用,即便應用服務器或者數據庫服務器其中一台宕機,整個系統的功能還是不會受到影響,但是這里會出現一個問題:例如當應用服務器1宕機了,所有的負載集中到應用服務器2上以后,因為系統功能是正常的,而作為產品交付客戶后也沒有現場的實施或運維人員在,我們並不知道有一台服務器已經宕機了,所以,我們要實現一個簡單的監控,去查看每個服務器上的每個應用程序是否正常的啟動着,當異常的時候可以通過網頁重新啟動一下.

實現思路

在我們的數據庫中建立兩張表,一張服務器表,一張應用表,服務器表作為主表
在服務器對應位置為每一個需要監控的應用放置一個監控的SHELL腳本,一個重啟的SHELL腳本
在WEB服務中增加定時任務,采用輪詢的方式執行監控腳本,如果返回值正常,則更新應用表中的狀態字段,同時在WEB頁面中加入一個監控頁面,可以在應用狀態異常的通過按鈕點擊完成重啟操作

技術要點

基本的增刪改查並沒有什么問題,在我的設計思路中有兩個點需要注意

  • SHELL腳本的編寫
    這個領域我並不了解,交由負責實施的同事解決
  • JAVA任務調度
    這部分我可能會單獨寫一篇博客來記錄一下我自己學習Spring Boot中的 @Scheduled注解和比較常用的Quartz框架的過程和對比
  • JAVA鏈接服務器並執行SHELL
    這里我采用的依賴包是:
<dependency>
    <groupId>ch.ethz.ganymed</groupId>
    <artifactId>ganymed-ssh2</artifactId>
    <version>262</version>
</dependency>

在有了這個依賴包之后,我就只用提供兩個共通的工具方法就可以了,一個是登錄一下服務器,看看服務器是否宕機,一個是執行SELL腳本的方法,根據SHELL的返回值確定監控的服務是否正常的方法就好了

代碼實現

因為執行SHELL腳本的時候可能是給出正常的返回值,也可能是異常的信息,這里需要一個JAVA Bean來接收這兩個消息,代碼如下:

import java.io.Serializable;

/**
 * shell腳本執行結果<br/>
 * 先通過getSuccess方法判斷命令是否執行成功<br/>
 * 執行成功的時候采用result作為返回值<br/>
 * 執行不成功采用errorOut作為返回值<br/>
 *
 * @author weizj
 */
public class ShellResult implements Serializable {

    private static final long serialVersionUID = -110281463872334425L;

    /** 腳本輸出結果 */
    private String result;
    /** 異常輸出結果 */
    private String errorMsg;
    /** 回話退出狀態 */
    private int exitStatus;

    public ShellResult() {
    }

    public ShellResult(String result, String errorOut, int exitStatus) {
        this.result = result.trim();
        this.errorMsg = errorOut.trim();
        this.exitStatus = exitStatus;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result.trim();
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg.trim();
    }

    public int getExitStatus() {
        return exitStatus;
    }

    public void setExitStatus(int exitStatus) {
        this.exitStatus = exitStatus;
    }

    /** 是否成功關閉會話 */
    public boolean getSuccess() {
        return this.exitStatus == 0;
    }
}

工具類方法代碼如下:

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * java執行shell腳本的工具類
 *
 * @author weizj
 */
public class GanymedUtils {
    
    private static final Logger logger = LoggerFactory.getLogger(GanymedUtils.class);
    
    /** 超時時間 */
    private static final int TIME_OUT = 1000 * 5 * 60;
    
    /**
     * 登錄遠端服務器
     *
     * @param ip       主機地址
     * @param userName 用戶名
     * @param password 密碼
     * @return 當前的連接
     * @throws IOException
     */
    public static Connection login(String ip, String userName, String password) throws IOException {
        Connection connection = new Connection(ip);
        connection.connect();
        return connection.authenticateWithPassword(userName, password) ? connection : null;
    }
    
    /**
     * 執行一個命令
     *
     * @param ip       主機ip
     * @param userName 用戶名
     * @param password 密碼
     * @param scripts  需要執行的腳本
     * @param charset  字符編碼
     * @return ShellResult類
     * @throws Exception
     */
    public static ShellResult exec(String ip, String userName, String password, String scripts, Charset charset) throws IOException {

        Connection connection = login(ip, userName, password);
        if (connection == null) {
            throw new RuntimeException("登錄遠程服務器出現異常,ip為:" + ip);
        }

        // Open a new {@link Session} on this connection
        Session session = connection.openSession();

        try (InputStream stdOut = new StreamGobbler(session.getStdout()); InputStream stdErr = new StreamGobbler(session.getStderr())) {
            // Execute a command on the remote machine.
            session.execCommand(scripts);
            String outStr = processStream(stdOut, charset.name());
            String outErr = processStream(stdErr, charset.name());
            session.waitForCondition(ChannelCondition.EXIT_STATUS, TIME_OUT);
            int exitStatus = session.getExitStatus();
            return new ShellResult(outStr, outErr, exitStatus);
        }
    }

    /**
     * 執行腳本
     *
     * @param in      輸入流
     * @param charset 字符編碼
     * @return
     * @throws IOException
     */
    private static String processStream(InputStream in, String charset) throws IOException {
        byte[] buf = new byte[1024];
        StringBuilder sb = new StringBuilder();
        while (in.read(buf) != -1) {
            sb.append(new String(buf, charset));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        try {
            ShellResult status = exec("10.0.0.1", "root", "root", "ipconfig", StandardCharsets.UTF_8);
            System.out.println(">>>>>>Result>>>>>>>");
            System.out.println(status.getResult());
            System.out.println(">>>>>>ErrorMsg>>>>>>>>");
            System.out.println(status.getErrorMsg());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

通過這兩個類再配合任務調度就可以基本的實現對應用的監控了.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM