公司的应用程序有时候会莫名其妙地挂掉,如果我们经常去登录服务器看是不是程序挂了,挂了再拉起,那样是非常耗时和麻烦的事情。
后来我们通过使用 supervisor 去守护启动,实现方法如下:
那什么是 supervisor了?
Supervisor是用 Python 开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统。它可以很方便地监听、启动、停止、重启一个或多个进程。用Supervisor管理的进程,当一个进程意外被杀死,或者是意外被停止(系统负载过高,cpu占用率很高等),supervisor 监听到进程死后,会自动将它重新拉起来,很方便地做到进程自动恢复的功能,不再需要自己写shell脚本来控制。一般情况下,yum直接安装即可。yum install supervisor
首先我们需要首先注意的一个地方是配置文件的后缀。
vim /etc/supervisord.conf
[include]

files = supervisord.d/*.ini

如果你想配置文件为其他格式,比如 conf 格式的话, 需要更改 iles = supervisord.d/*.conf 。
比如我们需要守护启动一个进程,我们就以守护Prometheus 为例:
vim /etc/supervisord.d/proms.ini

[program:proms]

command=/opt/prometheus/server/prometheus/prometheus

directory=/opt/prometheus/server/prometheus

stdout_logfile=/home/data/logs/prometheus/sever.log

autostart=true

autorestart=true

redirect_stderr=true

user=root

startsecs=3

supervisor配置文件详解:
program: 指定的守护进程名

command: 命令

stdout_logfile: 日志路径

autostart: supervisor启动的时候是否随着同时启动,默认为 true

autorestart: 是否挂了自动重启

redirect_stderr:标准错误重定向

startsecs: 子进程启动多少秒之后,此时的状态是running

启动supervisor--(yum方式安装的)
/usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf

或者

systemctl start supervisord.service

因此 我们可以使用如下的命令进行进程的停止,启动,重启等操作。

supervisorctl status # 查看应用启动状态

supervisorctl stop proms # 停止prometheus应用

supervisorctl start proms # 启动prometheus应用

supervisorctl restart proms # 重启prometheus应用

虽然使用上面的策略 supervisor, 可以实现进程的守护启动,如果进程挂了,会自动拉起,但是并没有告警通知的功能。所以我们需要监控进程的状态并实现告警 通知到对应的开发人员以及运维人员。对于这样的场景,我们如何去实现了?

分析
对于这种情况,我们可以使用如下的方案去实现:
方案一:使用 Zabbix/Prometheus监控系统,对Java应用程序做 TCP 端口检测。如果检测端口不通,就设置检测失败的触发器。然后实现告警.
方案二: 使用 Zabbix的自定义Key去实现告警,自定义key 的内容是执行一个shell脚本,Shell脚本用来检测进程是否存在,不存在的话,输出为 0,存在输出为1。然后Zabbix 的触发器 设置最近的T值 不为1,从而来实现进程状态检测的告警。
方案三:编写Python脚本用来检测进程的状态,然后使用Python的内置库,实现邮件告警。

解决
思路整理
这里我们重点讲下python如何检测。
1.首先Python程序需要检测 Java进程是否存在。
2.检测到进程如果存在不做任何处理,如果不存在,就需要触发邮件告警的函数
3.Python程序需要定时定期地去执行检测脚本。

代码实现
第一阶
首先我们先来实现判断进程是否存在的逻辑,判断进程是否存在,我们采用 psutil来实现。库的安装方法如下:
pip3 install psutil
检测进程存在的代码逻辑如下:

#coding:utf-8

import psutil

def checkprocess(processname):

pl = psutil.pids()

for pid in pl:

    if p.utilutil.Process.pid).name() == processname:

        return pid

if isinstance(checkprocess("notepad++.exe"),int):

print("进程存在")

else:

print(:进程不存在")

解析:
首先我们先定义一个 checkprocess 函数,函数的第一个参数传入进程名, 其中 pl = psutil.pids() 表示把所有的进程列出来。
接着我们for循环一下pid的列表,当找到 psutil.Process(pid).name() 的名词为传入的参数的名字的时候就返回pid值,没有就不做任何操作(可以认为返回内容为空)
接着 isinstance 用于检测返回内容。
那什么是 isinstance 了?
我们可以直接看看官方文档, https://docs.python.org/3/library/functions.html ,https://docs.python.org/3/library/functions.html

isinstance(object, classinfo)

Return True if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof. If object is not an object of the given type, the function always returns False. If classinfo is a tuple of type objects (or recursively, other such tuples), return True if object is an instance of any of the types. If classinfo is not a type or tuple of types and such tuples, a TypeError exception is raised.

我们翻译成中文,可以这样理解:
1.格式
isinstance(object,type-or-tuple-or-class) -> bool
2.作用
判断一个对象是某个类或子类的实例。
3.参数介绍
第一个参数(object)为对象,第二个参数(type)为类型名(int...)或类型名的一个列表((int,list,float)是一个列表)。其返回值为布尔型(True or flase)。
当第二个参数是type-or-tuple时
若第二个参数只有一个单独的类型,对象的类型与参数二的类型相同则返回True;
若第二个参数为一个元组类型,则若对象类型与元组中类型名之一相同即返回True。

第二阶段
我们在第一阶段实现了检测进程的相关代码,现在我们来实现发送邮件的代码实现,代码:内容如下:
#coding:utf-8

import smtplib

from email.mime.text import MIMEText

#第三方 SMTP 服务

mail_host = "smtp.exmail.qq.com" # SMTP服务器

mail_user = "tech.sys@aa.cn" # 用户名

mail_pass = "aapwd" # 密码

sender = 'tech.sys@aa.cn' # 发件人邮箱

#多个邮箱用逗号隔开构成列表

receivers = ['yyy@qq.com','xxx@qq.com'] # 接收人邮箱

定义函数。传入3个参数,第一个是接收者,第二个是主题,第三个是正文内容

def SendMail(receivers,title,content):

# content = '这是正文'

# title = '这是主题'  # 邮件主题

message = MIMEText(content, 'plain', 'utf-8')  # 内容, 格式, 编码

message['From'] = "{}".format(sender)

message['To'] = ",".join(receivers)

message['Subject'] = title

try:

    smtpObj = smtplib.SMTP_SSL(mail_host, 465)  # 启用SSL发信, 端口一般是465

    smtpObj.login(mail_user, mail_pass)  # 登录验证

    smtpObj.sendmail(sender, message['To'].split(','), message.as_string())  #邮件发"
    发" int("mail has been send successfully.")

except smtplib.SMTPException as e:

    print(e)

##测试邮件发送

SendMail(receivers,"主题","正文2")

解析:
上面的代码已经做了注释,代码功能不做详解。我们要注意的一点的是:smtpObj.sendmail(sender, message['To'].split(','), message.as_string())
因为之前我们的收件人,是列表的形式,所以在发送邮件的时候,我们需要 使用, 用 收件人进行逐一发送邮件。

第三阶段
整合以上两段代码,既可以检测进程又可以发送邮件:
#coding:utf-8

import smtplib

from email.mime.text import MIMEText

import psutil

#定义第三方 SMTP 服务

mail_host = "smtp.exmail.qq.com" # SMTP服务器

mail_user = "tech.sys@aa.cn" # 用户名

mail_pass = "aapwd" # 密码

sender = 'tech.sys@aa.cn' # 发件人邮箱

receivers = ['yy@qq.com','xx@qq.com'] ## 多个邮箱用逗号隔开构成列表

#定义进程名

P_name = "node_exporter"

#邮件发送函数

def SendMail(receivers,title,content):

# content = '这是正文'

# title = '这是主题'  # 邮件主题

message = MIMEText(content, 'plain', 'utf-8')  # 内容, 格式, 编码

message['From'] = "{}".format(sender)

message['To'] = ",".join(receivers)

message['Subject'] = title

try:

    smtpObj = smtplib.SMTP_SSL(mail_host, 465)  # 启用SSL发信, 端口一般是465

    smtpObj.login(mail_user, mail_pass)  # 登录验证

    smtpObj.sendmail(sender, message['To'].split(','), message.as_string())  # 发送

    print("mail has been send successfully.")

except smtplib.SMTPException as e:

    print(e)

#定义检测进程函数

def checkprocess(processname):

pl = psutil.pids()

for pid in pl:

    if psutil.Process(pid).name() == processname:

        return pid

#SendMail(receivers,"主题","正文2")

if isinstance(checkprocess(P_name),int):

pass   # 进程存在

else:

print("{0}进程不存在,发送邮件".format(P_name))

SendMail(receivers,"{0}进程down掉了".format(P_name),"{0}进程down掉了,请检测原因....".format(P_name))

解析:
上面的代码逻辑只是整合了两段代码, 并没有做其他的处理。首先是导入需要的模块,然后定义所需要的变量,以及函数,最后通过format函数将变量传入函数中而已。
需要注意的一点是,因为收件人是列表,那么在邮件发送的时候需要把列表进行切分,也就是使用split把收件人一个个地拿出来,然后再去进行邮件发送。

message['To'].split 切分出每个收件人(',') #split 切分出每个收件人

我们执行一下这段函数结果如下:

[root@me03 www]# python3 check_mail.py

node_exporter进程不存在,发送邮件

mail has been send successfully.
至此我们基本上实现了可以通过检测进程然后实现告警了。但是在我们编程当中,我们需要有模块化的编程思想,也就是有一些组件如果能模块化就进行模块化,那样子如果你有其他需求的话,想复用原来脚本的函数的话就不需要再去写重复的函数了。
所以我们可以通过类的方式进行导入。然后实现模块化的编程。

第四阶段
模块化编程,我们可以把邮件发送封装成一个类,用的时候直接导入即可。目录结构如下:
[www@me03 ~]$ tree ee/

ee/

├── check.py

└── S_mail.py

0 directories, 2 files

[www@me03 ~]$

S_mail.py 代码如下:
#coding:utf-8

import smtplib

from email.mime.text import MIMEText

class SendEMail(object):

# 定义第三方 SMTP 服务

def __init__(self):

    self.mail_host = "smtp.exmail.qq.com"  # SMTP服务器

    self.mail_user = "tech.sys@aa.cn"  # 用户名

    self.mail_pass = "aapwd"  # 密码

    self.sender = 'tech.sys@aa.cn'  # 发件人邮箱

    self.smtpObj = smtplib.SMTP_SSL(self.mail_host, 465)

    self.smtpObj.login(self.mail_user, self.mail_pass)  # 登录验证

def sendmail(self, receivers, title, content):

    message = MIMEText(content, 'plain', 'utf-8')  # 内容, 格式, 编码

    message['From'] = "{}".format(self.sender)

    message['To'] = ",".join(receivers)

    message['Subject'] = title

    try:

        self.smtpObj.sendmail(self.sender, message['To'].split(','), message.as_string())  # 发送

        print("mail has been send successfully.")

    except smtplib.SMTPException as e:

        print(e)

if name == 'main':

sm = SendEMail()

sm.sendmail(['1093381395@qq.com'], '主题', '正文')

解析:
构造函数中初始化了邮件发送人、 smtp服务器等等。mail邮件发送函数进行发送邮件。要注意sm.sendmail` 传入的收件人是列表
我们再看看check.py 的内容。

#coding:utf-8

from S_mail import SendEMail #导入邮件类

import psutil

#实例化邮件类

sm = SendEMail()

#定义收件人

receivers = ['1093381395@qq.com','xx@qq.com'] # 接收人邮箱

#定义进程名

P_name="node_exporter"

#定义检测进程函数

def checkprocess(processname):

pl = psutil.pids()

for pid in pl:

    if psutil.Process(pid).name() == processname:

        return pid

if isinstance(checkprocess(P_name),int):

pass   # 进程存在

else:

print("{0}进程不存在,发送邮件".format(P_name))

sm.sendmail(receivers,"{0}进程down掉了".format(P_name),"{0}进程down掉了,请检测原因....".format(P_name))

</code></pre>

check.py的代码就更加简单了,首先我们先是导入了邮件类,类的作用是用来发送邮件用的,然后实例化邮件类,再定义一些变量信息。比如收件人等。最后我们通过编写进程检测函数,用来检测进程,如果进程不存在则会调用邮件类里的邮件发送函数来实现告警

第四阶段:
我们实现了检测进程是否存在然后实现告警的代码,现在我们需要它定时去检测,然后实现告警。我们加到计划任务里去。这两个文件放到一个指定的目录 下。(一定要放到同一个目录下,不然无法导入。如果放到其他的目录的话,需要修改check.py的模块导入路径才行)
/1 * python3 /home/data/scripts/check.py
到此,我们通过定时执行脚本检测进程并实现告警的需求得以实现。

Python监控进程状态并实现告警的更多相关文章

  1. python基于pexpect库自动获取日志信息

    1. 前言对大部分的人来说,解决 Bug 都是依靠关键字去日志去定位问题!在调试情况下,我们可以实时在控制台查看日志;但对于部署到服务器上的应用,日志都存放在服务器某个目录下,没法通过本地查看到这种情况下,就需要我们先登录服务器,然后进入到日志目录文件夹,最后通过日志文件去定位问题;如果涉及到 K8......

  2. 解决python3 中的np.load编码问题

    由于在Python2 中的默认编码为ASCII,但是在Python3中的默认编码为UTF-8。问题:所以在使用np.load(det.npy)的时候会出现错误提示:you may need to pass the encoding= option to numpy.load解决方法:当遇到这种情况的......

  3. python基于opencv实现人脸识别

    将opencv中haarcascade_frontalface_default.xml文件下载到本地,我们调用它辅助进行人脸识别。识别图像中的人脸#coding:utf-8import cv2 as cv# 读取原始图像img = cv.imread('face.png')# 调用熟悉的人脸分类器 ......

  4. Python基础篇

    一、准备工作1、安装Python(注意选择一个稳定的版本,方便学习和使用)Python官网:https://www.python.org/2、安装一个anacondaanaconda官网:https://www.anaconda.com/安装完成之后在cmd里面输入conda -V检测,如果出现版本......

  5. Python 打印自己设计的字体的实例讲解

    通过对 26 个字母的设定,设置自己要输出的字体。name = "RUNOOB"# 接收用户输入# name = input("输入你的名字: \n\n") lngth = len(name) l = "" for x in range(0......

  6. python用分数表示矩阵的方法实例

    前言在机器学习中,我们会经常和矩阵打交道。在矩阵的运算中,python默认的输出是浮点数,但是如果我们想要矩阵的元素以分数的形式显示,可以通过添加一行代码来实现。1、函数及参数解释set_printoptions()——控制输出方式formatter——通用格式化输出Fraction(x).limi......

  7. Python 字符串去除空格的五种方法

    在处理Python代码字符串的时候,我们常会遇到要去除空格的情况,所以就总结了多种方法供大家参考。1、strip()方法去除字符串开头或者结尾的空格str = " Hello world "str.strip()输出:"Hello world"2、lstrip......

  8. python将YUV420P文件转PNG图片格式的两种方法

    方法一:import osimport cv2 as cvimport numpy as np# 读取yuv420p的一帧文件,并转化为png图片if __name__ == '__main__':filepath = 'one_frame_of_highway.yuv'binfile = open......

  9. Python 日志打印之logging.getLogger源码分析

    日志打印之logging.getLogger源码分析日志打印之logging.getLogger源码分析By:授客 QQ:1033553122 #实践环境WIN 10Python 3.6.5#函数说明logging.getLogger(name=None)getLogger函数位于logging/_......

  10. Python-zip()函数

    Python-zip()函数的一些相关知识Python内置help()的解释返回一个元组迭代器,其中第i个元组包含每个参数序列或可迭代对象中的第i个元素。当最短的可迭代输入耗尽时,迭代器将停止。使用单个可迭代参数,它将返回1元组的迭代器。没有参数,它将返回一个空的迭代器。 功能演示uppercase......

随机推荐

  1. Q-Q图原理详解及Python实现

    【导读】在之前的《数据挖掘概念与技术 第2章》的文章中我们介绍了Q-Q图的概念,并且通过调用现成的python函数, 画出了Q-Q图, 验证了Q-Q图的两个主要作用,1. 检验一列数据是否符合正态分布 2. 检验两列数据是否符合同一分布。本篇文章将更加全面的为大家介绍QQ图的原理以及自己手写函数实现......

  2. linux源码安装软件的一般方法

    rhel系统貌似安装不了xmgrace,配置的时候居然说要那个M*tif库。百度了一下,需要openmotif库,然后用root账户想要用yum安装一下这个库,搞了好久没搞懂。后面搞明白了,原因竟是因为所需要的库不支持rhel,枉我之前搞了这么久没安装好。不过也还好,安装这个破软件的过程让我更进一步......

  3. C# Twain协议调用扫描仪,设置多图像输出模式(Multi image output)

    Twain随着扫描仪、数码相机和其他图像采集设备的引入,用户热切地发现了将图像整合到他们的文档和其他工作中的价值。然而,支持这种光栅数据的显示和操作成本很高,应用程序开发人员需要创建用户界面并内置设备控制各种各样可用的图像设备。一旦他们的应用程序准备好支持给定的设备,他们就会面临一个令人沮丧的现实:......

  4. 如何不使用 overflow: hidden 实现 overflow: hidden

    一个很有意思的题目。如何不使用 overflow: hidden 实现 overflow: hidden?CSS 中 overflow 定义当一个元素的内容太大而无法适应块级格式化上下文时候该做什么。而 overflow: hidden 则会将超出容器范围内的内容剪裁。控制 overflow: hi......

  5. Java中ArrayList集合的常用方法大全

    ArrayList集合的创建非泛型创建ArrayList集合对象,可以添加任意Object子类元素至集合//非泛型创建的ArrayList集合对象可以保存任何类型的值ArrayList list = new ArrayList();list.add("str");//存入Stri......

  6. java中gc算法实例用法

    在我们对gc中的算法有基本概念理解后,要把算法的理念实现还需要依托实际垃圾收集器的使用。因为光靠一些简单的原理不足以支撑整个程序的运行,在回收机制上有专门的收集器。下面我们就垃圾收集器的概念、使用注意事项、收集器图解进行介绍,然后带来两种常见的垃圾收集器供大家参考。1.概念垃圾收集器时之前列举的垃圾......

  7. 复杂的数据类型

    目录一、列表 1.列表介绍 2.列表读取 读取单个元素 读取列表的一部分 3.列表的修改 4.其他常用操作列表的函数 二、字典 1.字典的介绍 2.字典的读取 3.字典的元素增减 一、列表1.列表介绍列表,数据类型为list,可以存储多个不同种类的元素,其中,字符串元素需要加引号,且元素间要用逗号隔......

  8. c#在sqlserver中使用EF框架

    vs2017,sqlserver2017(localdb)调试通过。在sqlserver中创建数据库d1,表t1如下:录入数据如下:在vs新建任意项目,此处以控制台为例。添加数据模型Model1:为了尽量少写代码,选择下图内容(该模式似乎没有迁移问题,挺好的):说明:连接字符串可以在代码中自行设置,......

  9. Python基础(中篇)

    数据类型的常用方法,条件语句,循环语句。本篇文章主要内容:数据类型的常用方法,条件语句,循环语句。在开始正篇之前我们先来看看上一篇留下的题目。题目:定义一个字典a,有两个键值对:一个键值对key是可乐,value是18;另一个键值对key是python,value是列表形式的1,2,3,4,5。答案......

  10. Java 执行过程中的内存模型

    一、前言本文的主要工作:尝试以时间顺序追踪一遍 Java 执行的整个过程,以及展示 JVM 中内存模型的相应变化。本文的主要目的:希望能够通过 Java 执行过程的冰山一角,增进对编程语言工作原理的理解。以下面这段代码为例,追踪它的执行过程:public class Car {private int......