Python multiprocessing multiprocess programming, interprocess communication, psutil monitors the process status and alarms through e-mail

Keywords: Python Linux Operation & Maintenance

The web backend on the server often needs to run multiple processes at the same time, and each process needs to exchange data. This function can be realized in python with the help of multiprocessing. Further, after the process is interrupted, we want developers to receive messages for improvement and maintenance at the first time, which needs to be implemented with the help of process management package psutil and Python's own e-mail sending module smtplib and email.

1, Multiprocessing programming and communication

In order to simulate multi process communication, we design three modules to realize this function: receiver, parser, and publish. The data is transmitted from the receiving module to the parsing module through the mutiprocessing Queue, and then from the parsing module to the publishing module.

The Receiver continuously generates natural numbers through a while loop and puts them in the queue:

def receiver(self, queue):                                                                            
    while True:                                                                                     
        self.count += 1                                                                             
        if not queue.full():                                                                        
            print(multiprocessing.current_process().pid, "Process playback data", self.count)                  
            queue.put(self.count)                                                                   
            time.sleep(1)                                                                           
        else:                                                                                       
            time.sleep(2) 

The parser further processes the data. It needs two queue parameters, one is responsible for receiving from the receiver and the other is responsible for transmitting to the publisher. When the data passed by the receiver is equal to 20, the program deliberately reports an error and the simulation process reports an error down.

def parser(self, queue, queue2):                                                                    
    while True:                                                                                     
        if not queue.empty():                                                                       
            num = queue.get()                                                                       
            if num == 20:       # Here, when num reaches 20, deliberately let the program report an error and simulate the process to report an error down                                                                    
                a = int("hello world")                                                              
                self.info = "Now count is %d" % a                                                   
                queue2.put(self.info)                                                               
            else:                                                                                   
                print(multiprocessing.current_process().pid, "Process receiving data", num)                   
                # lock.acquire()                                                                    
                self.info = "Now count is {}".format(num)                                           
                # lock.release()                                                                    
                queue2.put(self.info)                                                               
        else:                                                                                       
            pass

publisher is responsible for publishing data

def publish(self, queue):                                                                           
                                                                                                    
    while True:                                                                                     
        if not queue.empty():                                                                       
            msg = queue.get()                                                                       
            # lock.acquire()                                                                        
            print("process", multiprocessing.current_process().pid, "Publish info:", msg)              
            # lock.release()                                                                        
        else:                                                                                       
            pass

2, Process monitoring

analysis

In this case, we can use the following scheme:
Scheme 1: use the Zabbix/Prometheus monitoring system to detect the TCP port of Java applications. If the detection port is blocked, set the trigger for detection failure. Then realize the alarm
Scheme 2: use Zabbix's custom key to realize the alarm. The content of the custom key is to execute a shell script. The shell script is used to detect whether the process exists. If it does not exist, the output is 0 and the existence output is 1. Then, Zabbix's trigger sets the nearest T value not to be 1, so as to realize the alarm of process state detection.
Scheme 3: write Python script to detect the status of the process, and then use Python's built-in library to realize e-mail alarm.

Here we choose scheme 3, the function to be realized: to detect whether a process has become a zombie process
Idea:
1. First, the Python program needs to check whether the given process is running normally.
2. If it is detected that the process is normal, do not do any processing; If it has become a zombie process, you need to trigger the email alarm function
3.Python programs need to execute detection scripts regularly. (optional, I haven't implemented it here. It can be implemented by executing python files regularly through shell script, or python's own sleep, and then set it as a daemon in the linux background)

# Process detection function, cannot run alone
def checkprocess(self, target_pid):
    self.target_pid = target_pid
    pid1, pid2, pid3 = self.target_pid
    while True:
        print('Monitoring')
        process1 = psutil.Process(pid1)  # Instantiate three processes according to pid
        process2 = psutil.Process(pid2)
        process3 = psutil.Process(pid3)
        if process1.is_running and process1.status() != psutil.STATUS_ZOMBIE:
        # and is added here because the official document of psutil says, is_running will return the result of the zombie process to True. As for the specific status of the process, you can refer to the official documentation of psutil.
            pass
        else:
            self.sendmail(self.receiver, "Process monitoring", "receiver The process has become a zombie process!, Please check the reason")
        if process2.is_running and process2.status() != psutil.STATUS_ZOMBIE:
            pass
        else:
            self.sendmail(self.receiver, "Process monitoring", "parser The process has become a zombie process! Please check the reason")
        if process3.is_running and process3.status() != psutil.STATUS_ZOMBIE:
            pass
        else:
            self.sendmail(self.receiver, "Process monitoring", "receiver The process has become a zombie process! Please check the reason")
        time.sleep(5)

3, Python mail sending function

Use python to send e-mail and encapsulate it into a module using classes. You need to use python's own email and SMTP lib library

  • SMTP lib is responsible for creating the SMTP operation object, connecting to the SMTP target server, calling the methods in the object and sending mail to the target address
  • The email library is used to create mail objects (common mail objects include plain text mail objects, picture objects as attachments, and mail objects mixed with multiple objects)
  • You need to change your qq mailbox settings to log in to your qq mailbox and send mail with python agent. Here, you need to do two things: open the SMTP function of the mailbox and obtain the authorization code. Refer to: https://zhuanlan.zhihu.com/p/25565454
from email.mime.text import MIMEText
from email.header import Header
import smtplib

class SendEmail(object):

    def __init__(self):
        self.host_server = 'smtp.qq.com'
        # Sender's qq number
        self.sender_qq = 'xxxxx'               
        # The authorization code of QQ mailbox needs to receive the smtp service to open QQ mailbox. On the home page, it is set
        self.pwd = 'xxx'               
        self.sender_qq_mail = 'xxx@qq.com'       # Sender mailbox
        # Create an SMTP operation object and connect to the SMTP target server, which can be 163, QQ, etc
        self.smtp = smtplib.SMTP_SSL(self.host_server)
        
        # set_debuglevel() is used for debugging. A parameter value of 1 indicates that the debugging mode is turned on, and a parameter value of 0 indicates that the debugging mode is turned off
        # self.smtp.set_debuglevel(1)
        self.smtp.ehlo(self.host_server)
        self.smtp.login(self.sender_qq, self.pwd)         # validate logon

    def sendmail(self, receivers, mail_title, mail_content):
        msg = MIMEText(mail_content, "plain", 'utf-8') # Construct a text mail object
        # Set the header below
        # Mail subject
        msg["Subject"] = Header(mail_title, 'utf-8')
        # Sender
        msg["From"] = self.sender_qq_mail
        # addressee
        msg["To"] = ",".join(receivers)
        try:
        # msg.as_ As in string()_ String () is to change msg(MIMEText or MIMEMultipart object) to str.
            self.smtp.sendmail(self.sender_qq_mail, msg['To'].split(','), msg.as_string())
            print("mail has been post successfully")
        except smtplib.SMTPException as e:
            print(repr(e))

if __name__ == "__main__":
    sm = SendEmail()
    sm.sendmail(['xxx@163.com', 'xxx@gmail.com'], "python test","Hello, I'm working on a project python Sign in qq Test of e-mail from mailbox")

4, Complete code

The following is the complete code encapsulated into a class:
receiver.py

#  Create an analog receiver, generate data circularly and store it in the cache                                                                                                                                                                                                  
import time                                                                                             
import multiprocessing                                                                                  
                                                                                                        
class Recver:                                                                                           
                                                                                                        
    def __init__(self):                                                                                 
        self.count = 0                                                                                  
                                                                                                        
    def recver(self, queue):                                                                            
        while True:                                                                                     
            self.count += 1                                                                             
            if not queue.full():                                                                        
                print(multiprocessing.current_process().pid, "Process playback data", self.count)                  
                queue.put(self.count)                                                                   
                time.sleep(1)                                                                           
            else:                                                                                       
                time.sleep(2) 

parser.py

# Receive the data generated by the cycle, analyze it, and then store it in the cache                                                                                                                                                                                                     
import multiprocessing                                                                                  
                                                                                                                                                                                                          
class Parser:                                                                                           
    def __init__(self):                                                                                 
        self.info = "null"                                                                              
                                                                                                        
    def parser(self, queue, queue2):                                                                    
        while True:                                                                                     
            if not queue.empty():                                                                       
                num = queue.get()                                                                       
                if num == 20:                                                                           
                    a = int("hello world")                                                              
                    self.info = "Now count is %d" % a                                                   
                    queue2.put(self.info)                                                               
                else:                                                                                   
                    print(multiprocessing.current_process().pid, "Process receiving data", num)                   
                    # lock.acquire()                                                                    
                    self.info = "Now count is {}".format(num)                                           
                    # lock.release()                                                                    
                    queue2.put(self.info)                                                               
            else:                                                                                       
                pas

publisher.py

# Read from cache and publish                                                                                                                                                                                                                             
import multiprocessing                                                                                  
                                                                                                        
class Publisher():                                                                                      
    def __init__(self):                                                                                 
        pass                                                                                            
                                                                                                        
    def publish(self, queue):                                                                           
                                                                                                        
        while True:                                                                                     
            if not queue.empty():                                                                       
                msg = queue.get()                                                                       
                # lock.acquire()                                                                        
                print("process", multiprocessing.current_process().pid, "Publish info:", msg)              
                # lock.release()                                                                        
            else:                                                                                       
                pass

Monitoring script monitor.py

from email.mime.text import MIMEText
from email.header import Header
import smtplib
import psutil
import time


class Monitor(object):
    def __init__(self):
        self.target_pid = []           # Target pid to monitor
        self.host_server = 'smtp.qq.com'               # smtp service using QQ mailbox
        self.sender_qq = 'xxx'                  # Own QQ number
        self.pwd = 'xxxxx'                  # SMTP authorization code of QQ mailbox
        self.sender_qq_mail = 'xxx@qq.com'      # Sender mailbox, test my own QQ mailbox
        self.receiver = ['xxx'] # There can be multiple recipient mailboxes, which are written in the list and separated by commas
        self.smtp = smtplib.SMTP_SSL(self.host_server)  # Create an SMTP operation object and connect to the SMTP target server, which can be 163, QQ, etc
        # set_debuglevel() is used for debugging. A parameter value of 1 indicates that the debugging mode is turned on, and a parameter value of 0 indicates that the debugging mode is turned off
        # self.smtp.set_debuglevel(1)
        self.smtp.ehlo(self.host_server)
        self.smtp.login(self.sender_qq, self.pwd)        # validate logon

    def sendmail(self, receivers, mail_title, mail_content):
        # Constructing a MIMEText object represents a text mail object
        msg = MIMEText(mail_content, "plain", 'utf-8')
        # Set the header below
        # Mail subject
        msg["Subject"] = Header(mail_title, 'utf-8')
        # Sender
        msg["From"] = self.sender_qq_mail
        # addressee
        msg["To"] = ",".join(receivers)
        try:
            # msg.as_ As in string()_ String () is to change msg(MIMEText or MIMEMultipart object) to str.
            self.smtp.sendmail(self.sender_qq_mail, msg['To'].split(','), msg.as_string())
            self.smtp.quit()
            print("mail has been post successfully")
        except smtplib.SMTPException as e:
            print(repr(e))


    def checkprocess(self, target_pid):
        self.target_pid = target_pid
        pid1, pid2, pid3 = self.target_pid
        while True:
            print('Monitoring')
            process1 = psutil.Process(pid1)  # Instantiate three processes according to pid
            process2 = psutil.Process(pid2)
            process3 = psutil.Process(pid3)
            if process1.is_running and process1.status() != psutil.STATUS_ZOMBIE:
                # print('process1 status:', process1.status)
                pass
            else:
                self.sendmail(self.receiver, "Process monitoring", "receiver The process has become a zombie process!, Please check the reason")
            if process2.is_running and process2.status() != psutil.STATUS_ZOMBIE:
                pass
            else:
                self.sendmail(self.receiver, "Process monitoring", "parser The process has become a zombie process! Please check the reason")
            if process3.is_running and process3.status() != psutil.STATUS_ZOMBIE:
                pass
            else:
                self.sendmail(self.receiver, "Process monitoring", "receiver The process has become a zombie process! Please check the reason")
            time.sleep(5)

Start script start.py

from receiver import Recver                                                                                                                                                                                                                        
from parser import Parser                                                                               
from publisher import Publisher                                                                         
from monitor import Monitor                                                                             
import multiprocessing                                                                                  
                                                                                                                                                                                                                                                                                                                    
queue1 = multiprocessing.Queue(maxsize=5)                                                               
queue2 = multiprocessing.Queue(maxsize=5)                                                               
queue_pid = multiprocessing.Queue(maxsize=3)                                                            
                                                                                                        
                                                                                                                                                                                                                                                                                                         
if __name__ == '__main__':                                                                              
    jieshou = Recver()                                                                                  
    jiexi = Parser()                                                                                    
    publisher = Publisher()                                                                             
    monitor = Monitor()                                                                                 
    process1 = multiprocessing.Process(target=jieshou.recver, args=(queue1,), daemon=True)              
    process2 = multiprocessing.Process(target=jiexi.parser, args=(queue1, queue2), daemon=True)         
    process3 = multiprocessing.Process(target=publisher.publish, args=(queue2,), daemon=True)           
    process1.start()                                                                                    
    process2.start()                                                                                    
    process3.start()                                                                                    
    target_pid = (process1.pid, process2.pid, process3.pid)                                             
    process4 = multiprocessing.Process(target=monitor.checkprocess, args=(target_pid,), daemon=True) 
    process4.start()                                                                                    
    process1.join()                                                                                     
    # process2.join()

Posted by zizzy80 on Wed, 06 Oct 2021 11:10:46 -0700