linux 环境下监控thriftserver 运行内存(python)

最近发现thriftserver 运行时,运行内存有时超过配置文件 thriftserver.conf 中SPARK_EXCUTOR_MEM配置的内存,导致thriftserver执行查询异常。所以写了小程序,定时监控thriftserver的运行情况,当运行内存大于配置文件的内存时,将thriftserver重启。
 
1、配置远程ssh 命令执行接口。
因为spark 通常是集群部署,所以thriftserver的配置文件 或者 部署不是在我们所监控的机器上,所以不可避免需要远程执行命令。ssh 接口如下:
def ssh_execmd(hostname, port, username, password):
    client = paramiko.SSHClient()   # 需要 import paramiko 包
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
    try:
        client.connect(hostname=hostname, port=port, username=username, password=password)
        return client
    except Exception as e:
        return None
 
后面执行远程代码,只需要调用连接接口,用 stdin, stdout, stderr = conn.exec_command(cmd) 执行对应的cmd命令就可以了,三个变量分别记录 命令的输入、输出、错误信息。调用完之后,记得调用conn.close()方法,关闭已经建立的连接,避免重复建立白白消耗网络资源。
 
2、获取进程pid
根据应用名称获取进程的id 代号,有时间一个名称可能对应多个进程的id,这种情况在grep 后就需要通过awk 命令对结果进行过滤,按指定的规则输出。获取进程pid 代码如下:
 
def get_thrift_pid():
    cmd = "ps -ef |grep thriftserver|awk '$0 ~/memory/ {print $2}' | tr -s '\n' ' ' "
    (status, pid_list) = commands.getstatusoutput(" %s " %cmd)  #需要import commands 包
    result = commands.getoutput(cmd)
 
commands 包中的getstatusoutput 方法,执行linux 命令的时候返回两个变量,status表示命令执行状态,0表示成功,其他状态表示执行不成功。pid_list表示执行结果,可能有多个。
awk 的字段解释网上很多,也很详细,基本上就是对查找结果的一个过滤,这里的$0表示整个当前行,$2每行的第二个字段,~表示匹配后面的字符;同理,!~表示不匹配,这里的不是精确的比较。后面的tr -s 表示将输出的内容中包含的换行符替换成空格。
关于执行命令有多个pid返回,由于执行的python 代码中可能包含pid,所以执行时返回了一个当前python进程的pid,而这个pid不是我们真正需要的,怎么去除掉呢?我的做法是将这个命令执行两次,两次返回的结果中相同的那个pid就是我们真正把需要的进行pid,完整代码如下,最后返回的是我们需要的进程的pid:
 
def get_thrift_pid():
    cmd = "ps -ef |grep thriftserver|awk '$0 ~/memory/ {print $2}' | tr -s '\n' ' ' "
    (status, pid_list) = commands.getstatusoutput(" %s " %cmd)
    result = commands.getoutput(cmd)
    pid_list = pid_list.split(" ")[:-1]
    result_list = result.split(" ")[:-1]
    for pid in pid_list:
        if pid in result_list:
            return int(pid)
    return None
 
3、获取程序运行时的占用内存
根据前面获取的进程pid 获取当前进程执行时的资源占用情况,获取资源占用,可以根据top -p 进程号 ,在线查看当前pid的资源占用情况,当然此种方式不太适宜程序中直接读取。事实上,查看 /pro/进程号/statm 可以直接读取,这里的进程号就是获取的pid 号。查询占用内存的程序代码如下:
 
def  get_running_memory(pid):
    file_name = "/proc/%d/statm" %pid
    (status, output) = commands.getstatusoutput("cat %s" %file_name)
    use_mem = None
    if status == 0:
        [size, resident, shared, trs, lrs, drs, dt]  = output.split(" ")
        resident = int(resident)  # 字符串转换为整形
        use_mem  = resident * 4   # 使用内存, 每页占用4kb 内存
    return use_mem
 
读取statm获取的7个变量分别表示的含义如下:
size:任务虚拟地址空间大小
Resident:正在使用的物理内存大小
Shared:共享页数
Trs:程序所拥有的可执行虚拟内存大小
Lrs:被映像倒任务的虚拟内存空间的库的大小
Drs:程序数据段和用户态的栈的大小
dt:脏页数量
 
4、读取thriftserver配置文件
获取了thriftserver运行时的内存,还需要读取出thriftserver配置文件的内存,将两者进行比较。可能实际环境和我代码中的环境不一致,只需要修改ssh连接的信息即可,代码如下:
 
def get_thrift_conf():
    conf = ConfUtil().getThriftServerConf()   #这里是我机器封装的ConfUtil 类,别的机器可能不能运行!
    hostname = conf['ip']    #获取thriftserver 安装主机
    file_name = "cat /home/bsaworker/spark/conf/thriftserver.conf"
    conn = ssh_execmd(hostname, port, username, password)
    stdin, stdout, stderr = conn.exec_command(file_name)
    result = stdout.readlines()
    conn.close()
    conf_memroy= result[5].split('=')[1]
    conf_memroy = conf_memroy.replace('\n','')
    conf_memroy = conf_memroy.lower()
    if conf_memroy.find('t') !=-1:
        conf_memroy = int(conf_memroy.split('t')[0])*1024*1024*1024
    elif conf_memroy.find('g') !=-1:
        conf_memroy = int(conf_memroy.split('g')[0])*1024*1024
    elif conf_memroy.find('m') !=-1:
        conf_memroy = int(conf_memroy.split('m')[0])*1024 
    else:
        return 0
    return conf_memroy
 
这里单位统一转换成kb级别的,一般配置文件中不大可能给thriftserver配置kb级别的内存,所以在这里我直接过滤掉了。另外python可以ConfigParser类来读取或者操作配置文件,但是thriftserver的配置文件没有section 字段,所以无法直接进行读取了,我这里是自己写的一个方法,当然肯定还有其他方式。
 
5、比较完之后相关操作
运行时的内存和配置的内存都已经获取到了,接下来我们就要对获取的结果进行操作了,由于我们的需求是一直要监听,所以我这里写了个死循环,一直运行。当然也可以配置到cron中去,让程序定时运行。
 
def process_thrift():
    try:
        while True:
            pid = get_thrift_pid()
            running_memory = get_running_memory(pid)
            conf_memroy = get_thrift_conf()
            if running_memory > conf_memroy:
                commands.getoutput("kill -9 %d" %pid)
                print "running memory is exceed conf memory, the thriftserver will restart"
                time.sleep(100)   #100 秒后再执行
     # 执行restart 操作,因为我们这里只需要kill 就完了,所以就没有restart 命令,实际情况中可能restart 比较好
            else:
                time.sleep(30)    # 30秒之后再运行
                print "the thriftserver process is normal "
    except Exception as e:
        print "检测程序运行异常,exit! "
        return 
 
这里需要捕获异常信息了,处理方式还可以有其他方式,根据具体的需求可以再修改。
 
花了半天时间研究这个其实觉得还是蛮有趣的,先记录下来~

猜你喜欢

转载自www.cnblogs.com/lihao7/p/9145708.html