轉載別人博客,作個記錄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協議。
協議其實很簡單。
當supervisord啓動的時候,若是咱們的listener配置爲autostart=true的話,listener就會做爲supervisor的子進程被啓動。
listener被啓動以後,會向本身的stdout寫一個"READY"的消息,此時父進程也就是supervisord讀取到這條消息後,會認爲listener處於就緒狀態。
listener處於就緒狀態後,當supervisord產生的event在listener的配置的可接受的events中時,supervisord就會把該event發送給該listener。
listener接收到event後,咱們就能夠根據event的head,body裏面的數據,作一些列的處理了。咱們根據event的內容,判斷,提取,報警等等操做。
該乾的活都幹完以後,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