使用zt-exec庫定時清理linux休眠進程

在幾個月前上線的一個採集項目,構架是基於java + selenium + chromedriver + chrome實現的採集。至於爲哈不直接用jsoup或httpclient實現採集功能,是由於不少被採集頁面都是經過js來渲染內容的,因此必須用webdriver+chrome來模擬真正的瀏覽器訪問來採集。
每隔一段時間就會出採集失敗問題,出現的時間沒有規律,可能兩天出現一次,可能一星期出現一次,可能一個月出現一次....java

用linux top命令來查看服務器,會發現不少的chromedriver和chrome的進程linux

用ps命令查看服務器git

ps -aux | grep chrome

存在狀態爲Sl和Z的休眠進程和殭屍進程,啓動時間都不是當天,根據系統自己業務邏輯,進程不會存在運行那麼長時間的狀況。而java進程則所有都能正常關閉,但java進程啓動的chromedriver和chrome進程不必定能同時關閉,目前出現這種問題的緣由未找到。
最初想用命令把卡死的進程查出來批量殺掉github

ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}' | xargs kill -9   //殺死殭屍進程

結果發現只能查殺Z狀態的殭屍進程,Sl狀態的進程,一部分是正常的,一部分是須要殺死的(啓動時間爲Nov07,Nov06的進程須要殺掉),至於哪些須要殺死,須要經過人工判斷啓動時間來肯定是否須要殺掉進程。web

以前一直忙時,都是先經過ps命令把chromedriver和chrome相關進程查詢出來,而後經過人工判斷進程是否屬於休眠狀態,再手工kill殺掉進程。正則表達式

最近有空了,本着能程序解決,就毫不要人工維護,把以前的手工殺休眠進程操做程序化。一開始想直接經過java的Runtime.getRuntime().exec()代碼調用linux命令操做的,不過在java經常使用類庫中(https://www.21doc.net/java/awesomejava#processes),找到zt-exec庫,能夠簡化命令行調用操做。
程序化的代替人工維護實現定時清理休眠進程代碼以下:chrome

import org.apache.log4j.Logger;
import org.apache.log4j.RollingFileAppender;
import org.zeroturnaround.exec.ProcessExecutor;
import org.zeroturnaround.exec.stream.LogOutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ProcessKill {
    private static Logger log = Logger.getLogger(ProcessKill.class);
    public ProcessKill(){
    }

    public void run(){
        try{
            List<List<String>> list = new ArrayList<List<String>>();
//          先經過ps aux | grep chrome命令,獲取全部包含chrome文本內容的進程
            new ProcessExecutor().command("/bin/sh","-c","ps aux | grep chrome")
                    .redirectOutput(new LogOutputStream() {
                        protected void processLine(String line) {
                            log.info("line========" + line);
                            List<String> lines = split(line.trim());
//                          判斷進程啓動時間,肯定是否爲執行超時休眠的進程
                            String time = lines.get(8);
                            String format = "[0-5][0-9]:[0-5][0-9]";
                            Pattern p = Pattern.compile(format);
                            Matcher m = p.matcher(time);
                            boolean result = m.find();
                            if(result == true) {
                                log.info("time ========" + time + " " + result);
                            }
                            else{
                                log.info("time xxxxxxxx" + time + " " + result);
                                list.add(lines);
                            }
                        }
                    })
                    .execute();
            log.info("list========size:" + list.size());
            list.forEach(l->{
                log.info("list line========" + l);
            });
            for(int i = 0; i < list.size(); i++){
                List<String> strs = list.get(i);
                try{
                    String pid = strs.get(1);
                    String stat = strs.get(7);
                    String time = strs.get(8);
                    String cmd = strs.get(10);
                    log.info("pid========" + pid + ", " + stat + ", " + time + ", " + cmd);
					// 經過命令「kill -9 pid」殺掉進程
                    String output = new ProcessExecutor().command("/bin/sh","-c","kill -9 " + pid)
                            .readOutput(true).execute()
                            .outputUTF8();
                    log.info("kill========" + pid + ", " + output);
                }
                catch(Exception ex){
                    ex.printStackTrace();
                }
            }

        }
        catch(Exception e){
            e.printStackTrace();
        }
    }
	
    public List<String> split(String s){
//      ps aux | grep chrome 命令返回字段: USER,PID ,%CPU,%MEM,VSZ, RSS,TTY,STAT,START,TIME,COMMAND
        List<String> list = new ArrayList<String>();
        int blankCount = 0;
        StringBuffer sff = new StringBuffer();
        for(int i = 0; i < s.length(); i++){
            char c = s.charAt(i);
            if(list.size() < 10){
                if(c != ' '){
                    sff.append(c);
                    blankCount = 0;
                }
                else if(c == ' '){
                    blankCount++;
                }
                if(blankCount == 1){
                    list.add(sff.toString());
                    sff = new StringBuffer();
                }
            }
            else{
                sff.append(c);
            }
        }
        if(sff.length() > 0){
            list.add(sff.toString());
        }
        return list;
    }
    public static void setLogFile(String name){
        Logger rootLogger = Logger.getRootLogger();
        Enumeration en = rootLogger.getAllAppenders();
        while (en.hasMoreElements()){
            Object obj = en.nextElement();
            if(obj instanceof RollingFileAppender){
                RollingFileAppender file = (RollingFileAppender)obj;
                file.setFile(name + ".log");
                file.activateOptions();
            }
        }

    }

    public static void main(String[] args){
        setLogFile("log/" + ProcessKill.class.getSimpleName());
        new ProcessKill().run();
    }
}

 

文末記錄下ps和grep命令用法。apache

ps 顯示瞬間進程的狀態
參數:
-A :全部的進程均顯示出來,與 -e 具備一樣的效用;
-a : 顯示現行終端機下的全部進程,包括其餘用戶的進程;
-u :以用戶爲主的進程狀態 ;
x :一般與 a 這個參數一塊兒使用,可列出較完整信息。

ps aux 
USER:該進程屬於那個使用者帳號。
PID :該進程的進程ID號。
%CPU:該進程使用掉的 CPU 資源百分比;
%MEM:該進程所佔用的物理內存百分比;
VSZ :該進程使用掉的虛擬內存量 (Kbytes)
RSS :該進程佔用的固定的內存量 (Kbytes)
TTY :該進程是在那個終端機上面運做,若與終端機無關,則顯示。另外, tty1-tty6 是本機上面的登入者程序,若爲 pts/0 等等的,則表示爲由網絡鏈接進主機的程序。
STAT:該程序目前的狀態,主要的狀態有:
	R :該程序目前正在運做,或者是可被運做;
	S :該程序目前正在睡眠當中,但可被某些訊號(signal) 喚醒。
	T :該程序目前正在偵測或者是中止了;
	Z :該程序應該已經終止,可是其父程序卻沒法正常的終止他,形成 zombie (疆屍) 程序的狀態
START:該進程被觸發啓動的時間;
TIME :該進程實際使用 CPU 運做的時間。
COMMAND:該程序的實際指令。

ps -ef |grep java
UID     :程序被該 UID 所擁有
PID     :就是這個程序的 ID 
PPID    :則是其上級父程序的ID
C       :CPU使用的資源百分比
STIME   :系統啓動時間
TTY     :登入者的終端機位置
TIME    :使用掉的CPU時間。
CMD     :所下達的是什麼指令

 

grep命令的經常使用格式爲:grep  [選項]  」模式「  [文件]
經常使用選項:
  -E :開啓擴展(Extend)的正則表達式。
  -i :忽略大小寫(ignore case)。
  -v :反過來(invert),只打印沒有匹配的,而匹配的反而不打印。
  -n :顯示行號
  -w :被匹配的文本只能是單詞,而不能是單詞中的某一部分,如文本中有liker,而我搜尋的只是like,就可使用-w選項來避免匹配liker
  -c :顯示總共有多少行被匹配到了,而不是顯示被匹配到的內容,注意若是同時使用-cv選項是顯示有多少行沒有被匹配到。
  -o :只顯示被模式匹配到的字符串。
  --color :將匹配到的內容以顏色高亮顯示。
  -A  n:顯示匹配到的字符串所在的行及其後n行,after
  -B  n:顯示匹配到的字符串所在的行及其前n行,before
  -C  n:顯示匹配到的字符串所在的行及其先後各n行,context

查看系統狀態下的殭屍進程: 瀏覽器

ps -ef | grep defunct  後面尖括號裏是defunct的都是殭屍進程。 
ps aux | grep -w 'Z' 其中狀態爲Z的表明殭屍進程。
相關文章
相關標籤/搜索