supervisor-2:event

轉載別人博客,作個記錄java

原文連接:http://lixcto.blog.51cto.com/4834175/1540169python

 

supervisor的event機制其實,就是一個監控/通知的框架。拋開這個機制實現的過程來講的話,event其實就是一串數據,這串數據裏面有head和body兩部分。我們先弄清楚event數據結構,我們才能作後續的處理。先看看header長啥樣的吧程序員

1
ver: 3.0  server:supervisor serial: 21  pool:listener poolserial: 10  eventname:PROCESS_COMMUNICATION_STDOUT  len : 54

來講說上面的這個header每一項,都是什麼?shell

ver:表示event協議的版本,目前是3.0數據結構

server:表示supervisor的標識符,也就是我們上一篇中[supervisord]塊中的identifier選項中的東西框架

        默認爲supervisor運維

serial:這個東西是每一個event的序列號,supervisord在運行過程當中,發送的第一個event的序列號就是socket

        1,接下來的event依次類推ide

pool:這個是你的listener的pool的名字,通常你的listener只啓動一個進程的的話,其實也就沒有         pool的概念了。名字就是[eventlistener:theeventlistenername]這個東西spa

poolserial:上面的serial是supervisord給每一個event的編號。 而poolserial則是,eventpool給發送

             到我這個pool過來的event編的號

eventname:這個是event的類型名稱,這個後面說。

len:這個長度,表示的是header後面的body部分的長度。header以後,咱們會取len長度的內容做爲        body。

    好,說完了header,我們就該說說body部分的數據結構了。body的數據結構,實際上是和event的具體類型相關的,不一樣的event的類型,header的結構都同樣,可是body的結構大多就不同了。

關於event類型,我們就不展開說了,由於太多了,具體大夥能夠去參閱一下官網。其實搞會一個,其餘也都一個樣。

我們這裏說說待會一個要用到的類型就OK了,啥類型呢?

是PROCESS_STATE_EXITED

看着這名字,大夥差很少也就知道它是幹什麼的了。PROCESS_STATE_EXITED其實就是,當supervisord管理的子進程退出的時候,supervisord就會產生PROCESS_STATE_EXITED這麼個event。

來看看PROCESS_STATE_EXITED長啥樣吧,header我們前面說過了,都同樣。來看看body部分

1
processname:cat groupname:cat from_state:RUNNING expected: 0  pid: 2766

來講說具體含義

processname:就是進程名字,這裏名字不是咱們實際進程的名字,而是我們[program:x]配置成的名字

groupname:組名,這個一個樣

from_state:這個是,咱們的進程退出前的狀態是什麼狀態

expected:這個我們前面也講過,默認狀況下exitcodes是0和2,也就是說0和2是expected。其它的退出

          碼,也就是unexpected了

pid:這個大夥想必都知道。

    OK,說到了這裏,咱們知道了event的產生,而後給咱們的listener這麼一種結構的數據。

如今咱們有數據了,就看我們怎麼去處理這些數據了,這個過程就仁者見仁,智者見智了。咱們能夠利用接收的數據,加工後,進行報警,等等操做。

    處理數據以前,我們還得要來了解一下,listener和supervisord之間的通訊過程

    在這裏咱們首先要搞清楚,event的發起方和接收方。

    event的發起方是supervisord進程,接收方是一個叫listener的東西,listener怎麼配置,上一篇參數詳解裏面已經寫的很清楚了,大夥能夠去參考下,這裏就不贅述了。其實listener和program同樣,都是supervisord的子進程。二者的在配置上,不少選項也都同樣。

    其實,event還有另一個過程,咱們的program也就是咱們要管理的進程,也能夠發送event,進而和supervisord主動通訊。不過program程序通常都是程序員們搞,我們搞運維的就無論他們的事情了

OK,看看event協議。

協議其實很簡單。

  1. 當supervisord啓動的時候,若是咱們的listener配置爲autostart=true的話,listener就會做爲supervisor的子進程被啓動。

  2. listener被啓動以後,會向本身的stdout寫一個"READY"的消息,此時父進程也就是supervisord讀取到這條消息後,會認爲listener處於就緒狀態。

  3. listener處於就緒狀態後,當supervisord產生的event在listener的配置的可接受的events中時,supervisord就會把該event發送給該listener。  

  4. listener接收到event後,咱們就能夠根據event的head,body裏面的數據,作一些列的處理了。咱們根據event的內容,判斷,提取,報警等等操做。

  5. 該乾的活都幹完以後,listener須要向本身的stdout寫一個消息"RESULT\nOK",supervisord接受到這條消息後。就知道listener處理event完畢了。

 

好,來看看例子吧

#!/usr/bin/env python
#coding:utf-8
 
import sys
import os
import subprocess
#childutils這個模塊是supervisor的一個模型,能夠方便咱們處理event消息。。。固然咱們也能夠本身按照協議,用任何語言來寫listener,只不過用childutils更加簡便罷了
from supervisor import childutils
from optparse import OptionParser
import socket
import fcntl
import struct
 
__doc__ = "\033[32m%s,捕獲PROCESS_STATE_EXITED事件類型,當異常退出時觸發報警\033[0m" % sys.argv[0]
 
def write_stdout(s):
    sys.stdout.write(s)
    sys.stdout.flush()
#定義異常,沒啥大用其實
class CallError(Exception):
    def __init__(self,value):
        self.value = value
    def __str__(self):
        return repr(self.value)
#定義處理event的類
class ProcessesMonitor():
    def __init__(self):
        self.stdin = sys.stdin
        self.stdout = sys.stdout
 
    def runforever(self):
        #定義一個無限循環,能夠循環處理event,固然也能夠不用循環,把listener的autorestart#配置爲true,處理完一次event就讓該listener退出,而後supervisord重啓該listener,這樣listen#er就能夠處理新的event了
        while 1:
            #下面這個東西,是向stdout發送"READY",而後就阻塞在這裏,一直等到有event發過來
            #headers,payload分別是接收到的header和body的內容
            headers, payload = childutils.listener.wait(self.stdin, self.stdout)
            #判斷event是不是我們須要的,不是的話,向stdout寫入"RESULT\NOK",並跳過當前
            #循環的剩餘部分
            if not headers['eventname'] == 'PROCESS_STATE_EXITED':
                childutils.listener.ok(self.stdout)
                continue
 
            pheaders,pdata = childutils.eventdata(payload+'\n')
            #判讀event是不是expected是不是expected的,expected的話爲1,不然爲0
            #這裏的判斷是過濾掉expected的event
            if int(pheaders['expected']):
                childutils.listener.ok(self.stdout)
                continue
 
            ip = self.get_ip('eth0')
            #構造報警信息結構
            msg = "[Host:%s][Process:%s][pid:%s][exited unexpectedly fromstate:%s]" % (ip,pheaders['processname'],pheaders['pid'],pheaders['from_state'])
            #調用報警接口,這個接口是咱們公司本身開發的,大夥不能用的,要換成本身的接口
            subprocess.call("/usr/local/bin/alert.py -m '%s'" % msg,shell=True)
            #stdout寫入"RESULT\nOK",並進入下一次循環
            childutils.listener.ok(self.stdout)
 
 
    '''def check_user(self):
        userName = os.environ['USER']
        if userName != 'root':
            try:
                raise MyError('must be run by root!')
            except MyError as e:
                write_stderr( "Error occurred,value:%s\n" % e.value)
                sys.exit(255)'''
 
    def get_ip(self,ifname):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        inet = fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))
        ret = socket.inet_ntoa(inet[20:24])
        return ret
 
 
def main():
    parser = OptionParser()
    if len(sys.argv) == 2:
        if sys.argv[1] == '-h' or sys.argv[1] == '--help':
            print __doc__
            sys.exit(0)
    #(options, args) = parser.parse_args()
    #下面這個,表示只有supervisord才能調用該listener,不然退出
    if not 'SUPERVISOR_SERVER_URL' in os.environ:
        try:
            raise CallError("%s must be run as a supervisor event" % sys.argv[0])
        except CallError as e:
            write_stderr("Error occurred,value: %s\n" % e.value)
 
        return
 
    prog = ProcessesMonitor()
    prog.runforever()
 
if __name__ == '__main__':
    main()

差很少就這些了,其餘經常使用的event類型,已經listener的三種狀態,已經怎麼轉換的。大夥能夠去官網上看看

本文出自 「小城運維」 博客,請務必保留此出處http://lixcto.blog.51cto.com/4834175/1540169

相關文章
相關標籤/搜索