java程序監控tomcat實現項目宕機自動重啓併發送郵件提醒

最近因爲老項目頻繁掛掉,因爲項目通過多批人之手,短期難以定位問題,因此只好寫一個監控程序。 時間比較緊半天時間,並且水平有限大神勿噴,有好的方法還請賜教。 一、問題描述:分兩種狀況1.一、tomcat 完全掛掉端口不會佔用進程;1.二、並無完全掛掉 端口仍佔用進程,可是接口訪問異常; 二、解決思路:啓動一個java服務輪詢(10秒鐘一次)程序的一個接口(好比獲取當前時間),若是請求不到則查看該端口是否佔用進程,若是佔用則殺死進程,而後重啓tomcat ;若是沒有佔用進程則直接重啓tomca; 原本考慮的是監控多個程序的 可是時間問題就先監控一個吧java

3.1 輪訓接口linux

@Service
public class SchedulerService {
    private static final Logger logger = LoggerFactory.getLogger(SchedulerService.class);
    @Autowired
    private KillService killService;

    @Value("#{system['jiankong.ports']}")
    private String portsStr;
    @Value("#{system['url']}")
    private String url;

    /**
     * 監控接口是否通 若是接口不通 或者返回結果不對則重啓服務 併發送郵件 每10秒執行一次掃描
     * @author gaozemin
     * @date 2017年10月18日
     * @throws Exception
     * @return
     */
    public void watch() throws Exception {
        String[] ports = portsStr.split(",");
        for (String port : ports) {
            // 調用測試接口
            String ret = HttpUtil.sendPost(url, null);
            if (ret == null) {// 若是返回結果爲空重啓服務
                logger.info("返回結果爲null ");
                killService.start(Integer.valueOf(port));
            } else {
                try {
                    Map retMap = JSONObject.parseObject(ret, Map.class);
                    String retFlag = String.valueOf(retMap.get("result"));
                    if (!"200".equals(retFlag)) {// 若是返回結果異常 重啓服務
                        killService.start(Integer.valueOf(port));
                    } else {
                        logger.info("系統運行正常....");
                    }
                } catch (Exception e) {
                    logger.info("返回值解析異常....");
                    killService.start(Integer.valueOf(port));
                }
            }

            logger.info("監控執行中..");
        }

    }

3.2 若是監控到異常則重啓服務tomcat

@Service
public class KillService {
    private static final Logger logger = LoggerFactory.getLogger(KillService.class);

    @Value("#{system['waitTime']}")
    private Long waitTime;
    @Value("#{system['startTomcatExec']}")
    private String startTomcatExec;
    @Value("#{system['startLinuxTomcatExec']}")
    private String startLinuxTomcatExec;
    @Value("#{system['findPid']}")
    private String findPid;
    @Value("#{system['isLinux']}")
    private boolean isLinux;
    @Value("#{system['send.emails']}")
    private String emails;

    @Autowired
    private SendMail sendMail;

    private Map<Integer, Date> map = new HashMap();

    public void start(int port) {

        // 先每10秒 殺死一次進程 而後重啓一次 ,執行重啓後5分鐘後再從新執行掃描,確保程序從新啓動
        // 1 獲取 指定端口的進程號
        // 若是調用接口失敗則殺死進程並從新啓動 ,並記錄當前時間 ,不然不進行操做
        Date lastExecTime = map.get(port);
        if (lastExecTime != null) {// 若是存在重啓記錄則判斷重啓時間是否間隔5分鐘
            Date nowTome = new Date();
            Long subTime = nowTome.getTime() - lastExecTime.getTime();
            logger.info("間隔時間:{}", subTime);
            if (subTime < waitTime) {
                logger.info("間隔時間太短 等待程序啓動!");
                return;
            } else {
                map.put(port, new Date());
                restartTomcat(port, isLinux);
            }
        } else {
            map.put(port, new Date());
            restartTomcat(port, isLinux);
        }

    }

    private void restartTomcat(int port, boolean isLinux) {
        Runtime runtime = Runtime.getRuntime();
        try {
            if (isLinux) {
                // 查找進程號
                linuxRestart(port);
                StartTomcatThread a = new StartTomcatThread(startLinuxTomcatExec);
                a.start();
            } else {
                Process p = runtime.exec(findPid + port);
                InputStream inputStream = p.getInputStream();
                List<String> read = read(port, inputStream, "UTF-8");
                if (read.size() == 0) {
                    logger.info("找不到端口:{}的進程", port);
                    StartTomcatThread a = new StartTomcatThread(startTomcatExec);
                    a.start();
                    logger.info("tomcat已重啓");
                } else {
                    logger.info("找到" + read.size() + "個進程,正在準備清理");
                    kill(read);
                    StartTomcatThread a = new StartTomcatThread(startTomcatExec);
                    a.start();
                }
            }
            String dataStr = "admin 服務宕機  現已自動重啓 請及時查看日誌 修改錯誤!";
            String[] emailStrs = emails.split(",");
            for (String email : emailStrs) {
                sendMail.sendMsg(email, dataStr);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 驗證此行是否爲指定的端口,由於 findstr命令會是把包含的找出來,例如查找80端口,可是會把8099查找出來
     * 
     * @param str
     * @return
     */
    private boolean validPort(int port, String str) {
        String patternString = "^ *[a-zA-Z]+ +\\S+";
        Pattern pattern = Pattern.compile(patternString);
        Matcher matcher = pattern.matcher(str);

        matcher.find();
        String find = matcher.group();
        int spstart = find.lastIndexOf(":");
        find = find.substring(spstart + 1);

        int findport = 0;
        try {
            findport = Integer.parseInt(find);
        } catch (NumberFormatException e) {
            System.out.println("查找到錯誤的端口:" + find);
            return false;
        }
        if (port == findport) {
            return true;
        } else {
            return false;
        }
    }

    public void linuxRestart(int port) throws IOException, InterruptedException {
        String cmd = "kill -9 $(netstat -tlnp|grep " + port + "|awk '{print $7}'|awk -F '/' '{print $1}')";
        String[] command = { "sh", "-c", cmd };
        Process pro = Runtime.getRuntime().exec(command);
        pro.waitFor();
        // cmd = path;
        // pro = Runtime.getRuntime().exec(cmd);
        // pro.waitFor();
    }

    /**
     * 更換爲一個Set,去掉重複的pid值
     * 
     * @param data
     */
    public void kill(List<String> data) {
        Set<Integer> pids = new HashSet<>();
        logger.info("列表:{}" + pids);
        for (String line : data) {
            int offset = line.lastIndexOf(" ");
            String spid = line.substring(offset);
            spid = spid.replaceAll(" ", "");
            int pid = 0;
            try {
                pid = Integer.parseInt(spid);
            } catch (NumberFormatException e) {
                System.out.println("獲取的進程號錯誤:" + spid);
            }
            pids.add(pid);
        }
        killWithPid(pids);
    }

    /**
     * 一次性殺除全部的端口
     * 
     * @param pids
     */
    public void killWithPid(Set<Integer> pids) {
        for (Integer pid : pids) {
            try {
                Process process = Runtime.getRuntime().exec("taskkill /F /pid " + pid + "");
                InputStream inputStream = process.getInputStream();
                String txt = readTxt(inputStream, "UTF-8");
                logger.info(txt);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private List<String> read(int port, InputStream in, String charset) throws IOException {
        List<String> data = new ArrayList<>();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
        String line;
        while ((line = reader.readLine()) != null) {
            boolean validPort = validPort(port, line);
            if (validPort) {
                data.add(line);
            }
        }
        reader.close();
        return data;
    }

    public String readTxt(InputStream in, String charset) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
        StringBuffer sb = new StringBuffer();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        reader.close();
        return sb.toString();
    }
}

源代碼併發

相關文章
相關標籤/搜索