supervisor的event機(jī)制其實,就是一個監(jiān)控/通知的框架。拋開這個機(jī)制實現(xiàn)的過程來說的話蛉加,event其實就是一串?dāng)?shù)據(jù)同衣,這串?dāng)?shù)據(jù)里面有head和body兩部分竟块。咱們先弄清楚event數(shù)據(jù)結(jié)構(gòu),咱們才能做后續(xù)的處理耐齐。先看看header長啥樣的吧
ver:3.0 server:supervisor serial:21 pool:listener poolserial:10 eventname:PROCESS_COMMUNICATION_STDOUT len:54
來說說上面的這個header每一項浪秘,都是什么?
ver:表示event協(xié)議的版本埠况,目前是3.0
server:表示supervisor的標(biāo)識符耸携,也就是咱們上一篇中[supervisord]塊中的identifier選項中的東西 默認(rèn)為supervisor
serial:這個東西是每個event的序列號,supervisord在運行過程中辕翰,發(fā)送的第一個event的序列號就是1 接下來的event依次類推
pool:這個是你的listener的pool的名字违帆,一般你的listener只啟動一個進(jìn)程的的話,其實也就沒有pool的概念了金蜀。名字就是[eventlistener:theeventlistenername]這個東西
poolserial:上面的serial是supervisord給每個event的編號刷后。 而poolserial則是eventpool給發(fā)送到我這個pool過來的event編的號
eventname:這個是event的類型名稱,這個后面說渊抄。
-
len:這個長度尝胆,表示的是header后面的body部分的長度。header之后护桦,我們會取len長度的內(nèi)容作為body含衔。
好,說完了header,咱們就該說說body部分的數(shù)據(jù)結(jié)構(gòu)了二庵。body的數(shù)據(jù)結(jié)構(gòu)贪染,其實是和event的具體類型相關(guān)的,不同的event的類型催享,header的結(jié)構(gòu)都一樣杭隙,但是body的結(jié)構(gòu)大多就不一樣了。
關(guān)于event類型因妙,咱們就不展開說了痰憎,因為太多了,具體大伙可以去參閱一下官網(wǎng)攀涵。其實搞會一個铣耘,其他也都一個樣。
咱們這里說說待會一個要用到的類型就OK了以故,啥類型呢蜗细?
是PROCESS_STATE_EXITED
看著這名字,大伙差不多也就知道它是干什么的了怒详。PROCESS_STATE_EXITED其實就是炉媒,當(dāng)supervisord管理的子進(jìn)程退出的時候踪区,supervisord就會產(chǎn)生PROCESS_STATE_EXITED這么個event。
來看看PROCESS_STATE_EXITED長啥樣吧橱野,header咱們前面說過了,都一樣善玫。來看看body部分
processname:cat groupname:cat from_state:RUNNING expected:0 pid:2766
來說說具體含義
- processname:就是進(jìn)程名字水援,這里名字不是我們實際進(jìn)程的名字,而是咱們[program:x]配置成的名字
- groupname:組名茅郎,這個一個樣
- from_state:這個是蜗元,我們的進(jìn)程退出前的狀態(tài)是什么狀態(tài)
- expected:這個咱們前面也講過,默認(rèn)情況下exitcodes是0和2系冗,也就是說0和2是expected奕扣。其它的退出碼,也就是unexpected了
- pid:這個大伙想必都知道掌敬。
OK惯豆,說到了這里,我們知道了event的產(chǎn)生奔害,然后給我們的listener這么一種結(jié)構(gòu)的數(shù)據(jù)楷兽。
現(xiàn)在我們有數(shù)據(jù)了,就看咱們怎么去處理這些數(shù)據(jù)了华临,這個過程就仁者見仁芯杀,智者見智了。我們可以利用接收的數(shù)據(jù)雅潭,加工后揭厚,進(jìn)行報警,等等操作扶供。
處理數(shù)據(jù)之前筛圆,咱們還得要來了解一下,listener和supervisord之間的通信過程
在這里我們首先要搞清楚椿浓,event的發(fā)起方和接收方顽染。
event的發(fā)起方是supervisord進(jìn)程,接收方是一個叫l(wèi)istener的東西轰绵,listener怎么配置粉寞,上一篇參數(shù)詳解里面已經(jīng)寫的很清楚了,大伙可以去參考下左腔,這里就不贅述了唧垦。其實listener和program一樣,都是supervisord的子進(jìn)程液样。兩者的在配置上振亮,很多選項也都一樣巧还。
其實,event還有另外一個過程坊秸,我們的program也就是我們要管理的進(jìn)程麸祷,也可以發(fā)送event,進(jìn)而和supervisord主動通信褒搔。不過program程序一般都是程序員們搞阶牍,咱們搞運維的就不管他們的事情了
OK,看看event協(xié)議星瘾。
協(xié)議其實很簡單走孽。
當(dāng)supervisord啟動的時候,如果我們的listener配置為autostart=true的話琳状,listener就會作為supervisor的子進(jìn)程被啟動磕瓷。
listener被啟動之后,會向自己的stdout寫一個"READY"的消息,此時父進(jìn)程也就是supervisord讀取到這條消息后念逞,會認(rèn)為listener處于就緒狀態(tài)困食。
listener處于就緒狀態(tài)后,當(dāng)supervisord產(chǎn)生的event在listener的配置的可接受的events中時翎承,supervisord就會把該event發(fā)送給該listener陷舅。
listener接收到event后,我們就可以根據(jù)event的head审洞,body里面的數(shù)據(jù)莱睁,做一些列的處理了。我們根據(jù)event的內(nèi)容芒澜,判斷仰剿,提取,報警等等操作痴晦。
該干的活都干完之后南吮,listener需要向自己的stdout寫一個消息"RESULT\nOK",supervisord接受到這條消息后誊酌。就知道listener處理event完畢了部凑。
好,來看看例子吧
#!/usr/bin/env python
#coding:utf-8
import sys
import os
import subprocess
#childutils這個模塊是supervisor的一個模型碧浊,可以方便我們處理event消息涂邀。。箱锐。當(dāng)然我們也可以自己按照協(xié)議比勉,用任何語言來寫listener,只不過用childutils更加簡便罷了
from supervisor import childutils
from optparse import OptionParser
import socket
import fcntl
import struct
__doc__ = "\033[32m%s,捕獲PROCESS_STATE_EXITED事件類型,當(dāng)異常退出時觸發(fā)報警\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):
#定義一個無限循環(huán)浩聋,可以循環(huán)處理event观蜗,當(dāng)然也可以不用循環(huán),把listener的autorestart#配置為true衣洁,處理完一次event就讓該listener退出墓捻,然后supervisord重啟該listener,這樣listen#er就可以處理新的event了
while 1:
#下面這個東西坊夫,是向stdout發(fā)送"READY"砖第,然后就阻塞在這里,一直等到有event發(fā)過來
#headers,payload分別是接收到的header和body的內(nèi)容
headers, payload = childutils.listener.wait(self.stdin, self.stdout)
#判斷event是否是咱們需要的践樱,不是的話厂画,向stdout寫入"RESULT\NOK"凸丸,并跳過當(dāng)前
#循環(huán)的剩余部分
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')
#構(gòu)造報警信息結(jié)構(gòu)
msg = "[Host:%s][Process:%s][pid:%s][exited unexpectedly fromstate:%s]" % (ip,pheaders['processname'],pheaders['pid'],pheaders['from_state'])
#調(diào)用報警接口屎慢,這個接口是我們公司自己開發(fā)的瞭稼,大伙不能用的,要換成自己的接口
subprocess.call("/usr/local/bin/alert.py -m '%s'" % msg,shell=True)
#stdout寫入"RESULT\nOK"腻惠,并進(jìn)入下一次循環(huán)
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才能調(diào)用該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類型悔雹,已經(jīng)listener的三種狀態(tài),已經(jīng)怎么轉(zhuǎn)換的欣喧。大伙可以去官網(wǎng)上看看