深入理解Supervisor事件机制

Author Avatar
叠搭宝箱 2月 06, 2017

事件协议

事件机制是在supervisor v3.0开始引入的一个高级特性, 常用于守护程序崩溃时候的报警(发邮件/发短信)

该事件机制是一个简单的 Listener/Notification模型, Listener通过标准输入来获取supervisor发来的事件通知, 然后通过标准输出来告诉supervisor事件处理结果。过程中传递的EventNotification 由head和body两部分组成

可以先通过stdout输出一个 READY\n 字符串来表明开始接受事件, 然后通过 sys.stdin.readline() 来获取head信息, head的结构如下:

1
ver:3.0 server:supervisor serial:35 pool:event_listener poolserial:35 eventname:PROCESS_STATE_RUNNING len:91
  • ver: 版本信息
  • serial: supervisor给事件的编号, 第一个事件为1, 之后事件编号递增
  • eventpool: 产生event的event_listener名字
  • poolserial: 与serial不同的是, 由于可以有多个eventpool,而且eventpool可以检测的范围事件范围可以不同, 这个poolserial是相对某个eventpool的编号
  • eventname: supervisor 标准定义的事件状态
  • len: data长度, 此长度十分重要,需要再通过标准输入读入len长度的数据, 某个event_notification才算读取完毕

然后按照head的信息, 读入长度为len的数据, 这个数据就是event的data部分:

1
processname:application_demo_03 groupname:application_demo_03 from_state:STARTING pid:81292
  • processname: 触发事件的applicaiton名称
  • groupname: 触发事件的application的组名称
  • from_state: 事件触发状态之前的那个状态
  • pid: 进程id

处理完事件之后, 可以通过标准输出 RESULT 2\nOK 来告诉supervisor已经处理完事件

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import sys
import os
def write_stdout(s):
sys.stdout.write(s)
sys.stdout.flush()
def main():
while 1:
# transition from ACKNOWLEDGED to READY
write_stdout('READY\n')
# read header line and print it to stderr
line = sys.stdin.readline()
with open('event.log', 'a') as f:
f.write(line)
headers = dict([ x.split(':') for x in line.split() ])
data = sys.stdin.read(int(headers['len']))
with open('event.log', 'a') as f:
f.write(data)
f.write('\n\n')
write_stdout('RESULT 2\nOK')
if __name__ == '__main__':
main()

上边这种是比较原始事件处理方法, supervisor自带的childutils可以帮助你方便的处理事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import os
import sys
import json
from supervisor import childutils
def main():
while 1:
headers, payload = childutils.listener.wait(sys.stdin, sys.stdout)
with open('event.pro.log', 'a') as f:
f.write(json.dumps(headers))
childutils.listener.ok(sys.stdout)
if __name__ == '__main__':
main()

注意事项

  1. 在配置文件中配置event_listener的时候, 需要配置一个 events 的选项, 用以表明只监听某些类型的事件, 可以设置多个事件类型
  2. 监听器的处理事件流程为: readline()读取head -> 读取固定长度的data -> 输出状态信息 所以尽量避免在其中使用 print 等标准输出,,否则会破坏协议的完整性导致监听器失效, 如果想查看输出日志可以用文件或者网络传输等方式
  3. 测试代码