当前位置: 首页 > news >正文

使用zt-exec库定时清理linux休眠进程

为什么80%的码农都做不了架构师?>>>   hot3.png

在几个月前上线的一个采集项目,构架是基于java + selenium + chromedriver + chrome实现的采集。至于为哈不直接用jsoup或httpclient实现采集功能,是因为很多被采集页面都是通过js来渲染内容的,所以必须用webdriver+chrome来模拟真正的浏览器访问来采集。
每隔一段时间就会出采集失败问题,出现的时间没有规律,可能两天出现一次,可能一星期出现一次,可能一个月出现一次....

用linux top命令来查看服务器,会发现很多的chromedriver和chrome的进程

用ps命令查看服务器

ps -aux | grep chrome

存在状态为Sl和Z的休眠进程和僵尸进程,启动时间都不是当天,根据系统本身业务逻辑,进程不会存在运行那么长时间的情况。而java进程则全部都能正常关闭,但java进程启动的chromedriver和chrome进程不一定能同时关闭,目前出现这种问题的原因未找到。
最初想用命令把卡死的进程查出来批量杀掉

ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}' | xargs kill -9   //杀死僵尸进程

结果发现只能查杀Z状态的僵尸进程,Sl状态的进程,一部分是正常的,一部分是需要杀死的(启动时间为Nov07,Nov06的进程需要杀掉),至于哪些需要杀死,需要通过人工判断启动时间来确定是否需要杀掉进程。

之前一直忙时,都是先通过ps命令把chromedriver和chrome相关进程查询出来,然后通过人工判断进程是否属于休眠状态,再手工kill杀掉进程。

最近有空了,本着能程序解决,就绝不要人工维护,把之前的手工杀休眠进程操作程序化。一开始想直接通过java的Runtime.getRuntime().exec()代码调用linux命令操作的,不过在java常用类库中(https://www.21doc.net/java/awesomejava#processes),找到zt-exec库,可以简化命令行调用操作。
程序化的代替人工维护实现定时清理休眠进程代码如下:

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命令用法。

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的代表僵尸进程。

 

转载于:https://my.oschina.net/penngo/blog/2874158

相关文章:

  • 像素密度和分辨率
  • 链表的销毁与清空(转)
  • NChome如何创建单据跟主子表还有扩展开发要怎么弄?
  • 数据库 外存储器读写数据物理过程
  • android四大组件之Service 简单音乐播放器
  • 已安装pymysql 但Pycharm 中import pymysql出错的解决方案
  • 关于字符串的倒置
  • 实时计算Flink——独享模式——Batch功能介绍
  • jQuery页面加载初始化的3种方法
  • MetaMask/provider-engine-1
  • canvas-star7.html
  • 微信小程序上拉加载:onReachBottom详解+设置触发距离
  • ubuntu 安装配置ssh
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • idea tomcat启动时候中文乱码、问号
  • 2017 年终总结 —— 在路上
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • ES6系统学习----从Apollo Client看解构赋值
  • Java 23种设计模式 之单例模式 7种实现方式
  • JavaScript创建对象的四种方式
  • Markdown 语法简单说明
  • Selenium实战教程系列(二)---元素定位
  • spark本地环境的搭建到运行第一个spark程序
  • Theano - 导数
  • windows下使用nginx调试简介
  • 初探 Vue 生命周期和钩子函数
  • 电商搜索引擎的架构设计和性能优化
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 规范化安全开发 KOA 手脚架
  • 检测对象或数组
  • 简单易用的leetcode开发测试工具(npm)
  • 前端_面试
  • 前端工程化(Gulp、Webpack)-webpack
  • 微信小程序填坑清单
  • 学习HTTP相关知识笔记
  • 最简单的无缝轮播
  • 机器人开始自主学习,是人类福祉,还是定时炸弹? ...
  • ​iOS安全加固方法及实现
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • (1)常见O(n^2)排序算法解析
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (二)斐波那契Fabonacci函数
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (算法)Game
  • (算法)N皇后问题
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • (一)Java算法:二分查找
  • .gitattributes 文件
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .net和jar包windows服务部署
  • .Net下的签名与混淆
  • /etc/motd and /etc/issue