Skip to main content

事件

事件是3.0版本中引入的Supervisor的高级功能。你并不需要了解事件,如果你只是想用超级作为一种机制来重新启动崩溃的进程或作为一个系统来手动控制过程的状态。你需要了解的事件,如果你要使用Supervisor作为一个过程的监控/通知框架的一部分。

事件侦听器和事件通知

Supervisor为称为“事件监听器”的特殊编写程序(作为子进程运行)提供了一种方式来订阅“事件通知”。事件通知意味着发生了与由 supervisordsupervisord 本身控制的子进程相关的事情。事件通知被分组为类型,以便事件监听器能够订阅事件通知的有限子集。即使没有配置监听器,Supervisor也会继续发出事件通知。如果监听器配置并订阅了在 supervisord 生命周期内发出的事件类型,则将通知该监听器。

事件通知/订阅系统的目的是在满足某些条件时提供用于运行任意代码(例如发送电子邮件,发出HTTP请求等)的机制。该条件通常与子进程状态有关。例如,当进程崩溃并由Supervisor重新启动时,您可能希望通过电子邮件通知某人。

事件通知协议基于通过子过程“stdin和stdout”的通信。 Supervisor将特殊格式的输入发送到事件侦听器进程的stdin,并期望来自事件侦听器的stdout的特殊格式的输出,形成请求 - 响应周期。在supervisor和监听器的实现者之间商定的协议允许监听器处理事件通知。事件侦听器可以用您用于运行Supervisor的平台支持的任何语言编写。尽管事件监听器可以用任何语言编写,但是以 supervisor.childutils 模块的形式提供对Python的特殊库支持,这使得在Python中创建事件监听器比在其他语言中更容易。

配置事件监听器

通过配置文件中的 [eventlistener:x] 部分指定管理程序事件侦听器。除了管理程序不遵守事件监听器进程的“捕获模式”输出(即,事件监听器不能是 PROCESS_COMMUNICATIONS_EVENT 事件生成器)之外,管理程序 [eventlistener:x] 部分几乎完全像处理器 [program:x] 部分一样处理它们的配置中允许的密钥。因此,在事件监听器的配置中指定 stdout_capture_maxbytesstderr_capture_maxbytes 是错误的。对可以放入配置文件中的事件监听器部分的数量没有人为约束。

当定义 [eventlistener:x] 段时,它实际上定义了“池”,其中池中的事件监听器的数量由段内的 numprocs 值确定。

[eventlistener:x] 部分的 events 参数指定将发送到侦听器池的事件。编写良好的事件侦听器将忽略它无法处理的事件,但不能保证特定事件侦听器不会因接收到无法处理的事件类型而崩溃。因此,根据侦听器实现,在配置中指定它可以仅接收某些类型的事件可能是重要的。事件监听器的实现者是唯一能告诉你这些是什么(因此在 events 配置中有什么价值)的人。可以放置在 supervisord.conf 中的事件监听器配置的示例如下。

[eventlistener:memmon]
command=memmon -a 200MB -m bob@example.com
events=TICK_60
[eventlistener:mylistener]
command=my_custom_listener.py
events=PROCESS_STATE,TICK_60

注解

可以通过 pkg_resources “入口点”字符串形式的 [eventlistener:x] 段的 result_handler 参数来指定高级功能,指定池的备用“结果处理程序”。默认结果处理程序是 supervisord.dispatchers:default_handler。创建备用结果处理程序当前未记录。

当supervisor发送事件通知时,将找到所有预订接收事件类型的事件(由事件监听器部分中的 events 值过滤)的事件监听器池。每个侦听器池中的侦听器之一将接收事件通知(任何“可用”侦听器)。

事件侦听器池中的每个进程由管理员平等对待。如果池中的进程不可用(因为它已经在处理事件,因为它已经崩溃,或者因为它已经选择从池中删除它自己),管理员将从池中选择另一个进程。如果无法发送事件,因为池中的所有侦听器都“忙”,则事件将被缓冲,并且稍后将重试通知。 “稍后”被定义为“下一次 supervisord 选择循环执行”。为了令人满意的事件处理性能,您应该配置一个池,其中包含适当的事件侦听器进程以处理事件加载。这只能根据任何给定工作负载凭经验确定,没有“魔术数字”,但是为了帮助您确定给定池中的最佳监听器数量,当事件无法立即发送时,Supervisor将向其活动日志发出警告消息以缓冲拥塞。没有对池中可以存在的进程数量施加人为约束,它仅受平台约束的限制。

侦听器池具有事件缓冲区队列。队列的大小通过侦听器池的 buffer_size 配置文件选项。如果队列已满并且supervisor尝试缓冲事件,则超级用户将丢弃缓冲区中最旧的事件并记录错误。

编写事件侦听器

事件监听器实现是一个愿意在其stdin流上接受结构化输入并在其stdout流上产生结构化输出的程序。事件监听器实现应该在“无缓冲”模式下运行,或者每次它需要与supervisord进程通信时刷新它的stdout。事件侦听器可以写为长时间运行或可以在单个请求后退出(取决于事件侦听器配置中的实现和 autorestart 参数)。

事件监听器可以向其stderr发送任意输出,根据其 [eventlistener:x] 部分中与stderr相关的日志文件配置,superderord将记录或忽略该输出。

事件通知协议

当supervisord向事件侦听器进程发送通知时,首先在侦听器的stdin上发送单个“标题”行。线的组成是由单个空间彼此分离的一组冒号分隔的令牌(每个表示键值对)。该行以 \n (换行)字符结束。线上的令牌不能保证以任何特定的顺序。当前定义的令牌类型在下表中。

标题令牌

描述

ver

事件系统协议版本

3.0

服务器

发送事件的超级用户的标识符(参见配置文件 [supervisord] section identifier value。

 

串行

分配给每个事件的整数。在 supervisord 过程的生命周期内生成的两个事件将没有相同的序列号。该值对于功能测试和检测事件排序异常非常有用。

30

生成此事件的事件侦听器池的名称。

myeventpool

poolserial

由发送它的事件监听器池分配给每个事件的整数。在 supervisord 过程的生存期内,相同事件列表池生成的两个事件将具有相同的 poolserial 编号。此值可用于检测事件排序异常。

30

事件名

特定事件类型名称(请参阅 事件类型

TICK_5

len

一个整数,指示事件有效内容中的字节数,也称为 PAYLOAD_LENGTH

22

完整标题行的示例如下。

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

紧跟在标题中的换行符后面的是事件有效载荷。它由代表事件数据的序列化的 PAYLOAD_LENGTH 字节组成。有关特定事件数据序列化定义,请参阅 事件类型

PROCESS_COMMUNICATION_STDOUT 事件通知的示例有效载荷如下。

processname:foo groupname:bar pid:123
This is the data that was sent between the tags

任何给定事件的有效载荷结构仅由事件的类型确定。

事件侦听器状态

事件侦听器进程具有由supervisord维护的三种可能状态:

名称

描述

确认

事件侦听器已确认(接受或拒绝)事件发送。

准备

事件通知可以发送到此事件侦听器

事件通知可能不会发送到此事件侦听器。

当事件侦听器进程首次启动时,管理程序会自动将其置于 ACKNOWLEDGED 状态,以允许启动活动或防止启动失败(挂起)。直到侦听器发送 READY\n 字符串到它的stdout,它将保持在这个状态。

当监管者向 READY 状态中的监听器发送事件通知时,监听器将被置于 BUSY 状态,直到它从监听器接收到 OKFAIL 响应,此时,监听器将被转换回 ACKNOWLEDGED 状态。

事件侦听器通知协议

Supervisor将通过向进程的stdin发送数据来通知事件的 READY 状态的事件侦听器。当该进程处于 BUSYACKNOWLEDGED 状态时,Supervisor不会向事件监听器进程的stdin发送任何东西。管理员通过发送头来启动。

一旦它处理了头部,事件监听器实现应该从它的stdin读取 PAYLOAD_LENGTH 字节,根据头部中的值和解析出的序列化数据执行任意操作。在这样做时,可以随意阻挡任意时间。 Supervisor将在其等待响应时继续正常处理,并且将根据需要将相同类型的其他事件发送到同一池中的其他侦听器进程。

在事件监听器处理事件序列化之后,为了通知supervisord关于结果,它应该在其stdout上发送一个结果结构。结果结构是单词“RESULT”,后跟一个空格,后跟结果长度,后跟换行符,后跟结果内容。例如,RESULT 2\nOK 是结果“OK”。通常,事件监听器将使用 OKFAIL 作为结果内容。这些字符串对默认结果处理程序有特殊的意义。

如果默认结果处理程序接收到 OK 作为结果内容,它将假设侦听器成功处理了事件通知。如果它接收到 FAIL,它将假设侦听器未能处理该事件,并且事件将被重新缓冲并在稍后的时间再次发送。事件侦听器可以通过返回 FAIL 结果以任何原因拒绝事件。这并不表示事件数据或事件侦听器有问题。一旦超级接收器接收到 OKFAIL 结果,则事件侦听器被置于 ACKNOWLEDGED 状态。

一旦监听器处于 ACKNOWLEDGED 状态,它可以退出(并且如果其 autorestart 配置参数是 true,则随后可以由管理器重新启动),或者它可以继续运行。如果它继续运行,为了被supervisord重新放置到 READY 状态,它必须发送一个 READY 令牌,然后立即通过一个换行符到它的stdout。

事件侦听器实现示例

一个“长时间运行”事件监听器的Python实现,它接受一个事件通知,将标题和有效负载打印到它的stderr,并以 OK 结果响应,然后 READY 如下。

import sys

def write_stdout(s):
    # only eventlistener protocol messages may be sent to stdout
    sys.stdout.write(s)
    sys.stdout.flush()

def write_stderr(s):
    sys.stderr.write(s)
    sys.stderr.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()
        write_stderr(line)

        # read event payload and print it to stderr
        headers = dict([ x.split(':') for x in line.split() ])
        data = sys.stdin.read(int(headers['len']))
        write_stderr(data)

        # transition from READY to ACKNOWLEDGED
        write_stdout('RESULT 2\nOK')

if __name__ == '__main__':
    main()

其他示例事件侦听器存在于 superlance 软件包中,包括可监视管理程序子进程,并在使用“太多”内存时重新启动进程。

事件侦听器错误条件

如果事件侦听器进程在事件被发送到其标准输入时死亡,或者如果它在将结果结构发送回超级驾驶员之前死亡,则假定事件不被处理,并且将由超级驾驶员重新缓冲并且稍后再次发送。

如果事件监听器基于事件监听器所在的状态向其stdout发送数据,该监听器不能识别为适当的响应,则事件监听器将被置于 UNKNOWN 状态,并且不会向其发送进一步的事件通知。如果在这段时间内侦听器正在处理一个事件,它将被重新缓冲,并在稍后再次发送。

事件侦听器可以使用Supervisor XML-RPC接口调用“回到”Supervisor。因此,作为接收事件通知的结果,事件监听器可以影响Supervisor子进程的状态。例如,您可能需要每隔几分钟与Supervisor控制的子进程的进程使用情况生成一个事件,如果这些进程中的任何进程超过一些内存阈值,您想要重新启动它。您将编写一个程序,导致supervisor每隔一段时间生成 PROCESS_COMMUNICATION 事件,并在其中包含内存信息,以及一个事件监听器,用于根据处理从这些事件接收的数据执行操作。

事件类型

事件类型是由Supervisor自己定义的受控集合。没有办法添加事件类型而不改变 supervisord 本身。这通常不是问题,因为元数据附加到事件可以由事件监听器作为附加过滤器标准,结合其类型使用。

可以由事件监听器预订的事件类型由supervisor预定义并且分为几个主要类别,包括“进程状态改变”,“进程通信”和“supervisor状态改变”事件。以下是描述这些事件类型的表。

在下面的列表中,我们指出一些事件类型有一个“body”,它是一个 令牌集。令牌集包括一组具有空格分隔令牌的charaters。每个令牌表示一个键值对。键和值由冒号分隔。例如:

processname:cat groupname:cat from_state:STOPPED

令牌集在其末尾没有换行符或回车符。

EVENT 事件类型

基本事件类型。此事件类型是抽象的。它永远不会直接发送。订阅此事件类型将导致订阅者接收Supervisor发出的所有事件通知。

NameEVENT

子类型:N/A

身体描述:N/A

PROCESS_STATE 事件类型

此进程类型表示进程已从一个状态移动到另一个状态。有关过程在其生命周期中经历的状态的描述,请参阅 过程国。这个事件类型是抽象的,它永远不会被直接发送。订阅此事件类型将导致订阅者接收作为 PROCESS_STATE 的子类型的所有事件类型的事件通知。

NamePROCESS_STATE

子类型EVENT

身体描述

PROCESS_STATE 的所有子类型都有一个令牌集。此外,每个 PROCESS_STATE 子类型的令牌集具有默认的一组键/值对:processnamegroupnamefrom_stateprocessname 表示supervisor知道此进程的进程名称。 groupname 表示此进程所在的超级用户组的名称。from_state 是此进程正在转换的状态的名称(新状态由具体事件类型隐含)。具体子类型可以包括令牌集中的附加键/值对。

PROCESS_STATE_STARTING 事件类型

表示进程已从状态切换到STARTING状态。

NamePROCESS_STATE_STARTING

子类型PROCESS_STATE

身体描述

这个主体是一个令牌集。它具有默认的键/值对加上一个附加的 tries 键。 tries 表示此进程在转换到RUNNING或FATAL之前已进入此状态的次数(它永远不会大于进程的“startretries”参数)。例如:

processname:cat groupname:cat from_state:STOPPED tries:0

PROCESS_STATE_RUNNING 事件类型

表示进程已从 STARTING 状态转移到 RUNNING 状态。这意味着就Supervisor而言,该流程已成功启动。

NamePROCESS_STATE_RUNNING

子类型PROCESS_STATE

身体描述

这个主体是一个令牌集。它具有默认的键/值对加上一个附加的 pid 键。 pid 表示已启动的进程的UNIX进程标识。例如:

processname:cat groupname:cat from_state:STARTING pid:2766

PROCESS_STATE_BACKOFF 事件类型

表示进程已从 STARTING 状态转移到 BACKOFF 状态。这意味着进程未成功进入RUNNING状态,并且Supervisor将尝试重新启动它,除非它已超过其“startretries”配置限制。

NamePROCESS_STATE_BACKOFF

子类型PROCESS_STATE

身体描述

这个主体是一个令牌集。它具有默认的键/值对加上一个附加的 tries 键。 tries 表示此进程在转换到 RUNNINGFATAL 之前已进入此状态的次数(它永远不会大于进程的“startretries”参数)。例如:

processname:cat groupname:cat from_state:STOPPED tries:0

PROCESS_STATE_STOPPING 事件类型

表示进程已从 RUNNING 状态或 STARTING 状态转移到 STOPPING 状态。

NamePROCESS_STATE_STOPPING

子类型PROCESS_STATE

身体描述

这个主体是一个令牌集。它具有默认的键/值对加上一个附加的 pid 键。 pid 表示已启动的进程的UNIX进程标识。例如:

processname:cat groupname:cat from_state:STARTING pid:2766

PROCESS_STATE_EXITED 事件类型

表示进程已从 RUNNING 状态转移到 EXITED 状态。

NamePROCESS_STATE_EXITED

子类型PROCESS_STATE

身体描述

这个主体是一个令牌集。它具有默认的键/值对加上两个附加键:pidexpectedpid 表示退出的进程的UNIX进程标识。 expected 表示过程是否以预期的退出代码退出。如果退出代码是意外的,则它将是 0,如果期望退出代码,则是 1。例如:

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

PROCESS_STATE_STOPPED 事件类型

表示进程已从 STOPPING 状态转移到 STOPPED 状态。

NamePROCESS_STATE_STOPPED

子类型PROCESS_STATE

身体描述

这个主体是一个令牌集。它具有默认的键/值对加上一个附加的 pid 键。 pid 表示已启动的进程的UNIX进程标识。例如:

processname:cat groupname:cat from_state:STOPPING pid:2766

PROCESS_STATE_FATAL 事件类型

表示进程已从 BACKOFF 状态转移到 FATAL 状态。这意味着Supervisor尝试 startretries 次数不成功启动进程,并放弃了尝试重新启动它。

NamePROCESS_STATE_FATAL

子类型PROCESS_STATE

身体描述

此事件类型是使用默认键/值对的令牌集。例如:

processname:cat groupname:cat from_state:BACKOFF

PROCESS_STATE_UNKNOWN 事件类型

表示进程已从任何状态转移到 UNKNOWN 状态(表示 supervisord 中的错误)。只有在 supervisord 本身有编程错误时,才会发生这种状态转换。

NamePROCESS_STATE_UNKNOWN

子类型PROCESS_STATE

身体描述

此事件类型是使用默认键/值对的令牌集。例如:

processname:cat groupname:cat from_state:BACKOFF

REMOTE_COMMUNICATION 事件类型

在Supervisor的RPC接口上调用 supervisor.sendRemoteCommEvent() 方法时引发的事件类型。 typedata 是RPC方法的参数。

NameREMOTE_COMMUNICATION

子类型EVENT

身体描述

type:type
data

PROCESS_LOG 事件类型

当进程写入stdout或stderr时发出的事件类型。仅当文件描述符不处于捕获模式并且 stdout_events_enabledstderr_events_enabled 配置选项设置为 true 时,才会发出事件。这个事件类型是抽象的,它永远不会被直接发送。订阅此事件类型将导致订阅者接收 PROCESS_LOG 的所有子类型的事件通知。

NamePROCESS_LOG

子类型EVENT

身体描述:N/A

PROCESS_LOG_STDOUT 事件类型

表示进程已写入其stdout文件描述符。仅当文件描述符不处于捕获模式并且 stdout_events_enabled 配置选项设置为 true 时,才会发出该事件。

NamePROCESS_LOG_STDOUT

子类型PROCESS_LOG

身体描述

processname:name groupname:name pid:pid
data

PROCESS_LOG_STDERR 事件类型

表示进程已经写入其stderr文件描述符。仅当文件描述符不处于捕获模式并且 stderr_events_enabled 配置选项设置为 true 时,才会发出该事件。

NamePROCESS_LOG_STDERR

子类型PROCESS_LOG

身体描述

processname:name groupname:name pid:pid
data

PROCESS_COMMUNICATION 事件类型

当任何进程尝试在其输出中的 <!--XSUPERVISOR:BEGIN--><!--XSUPERVISOR:END--> 标记之间发送信息时引发的事件类型。这个事件类型是抽象的,它永远不会被直接发送。订阅此事件类型将导致订阅者接收 PROCESS_COMMUNICATION 的所有子类型的事件通知。

NamePROCESS_COMMUNICATION

子类型EVENT

身体描述:N/A

PROCESS_COMMUNICATION_STDOUT 事件类型

表示进程已经向其stdout文件描述符发送了一个消息给Supervisor。

NamePROCESS_COMMUNICATION_STDOUT

子类型PROCESS_COMMUNICATION

身体描述

processname:name groupname:name pid:pid
data

PROCESS_COMMUNICATION_STDERR 事件类型

表示进程已经向其stderr文件描述符发送了一个消息给Supervisor。

NamePROCESS_COMMUNICATION_STDERR

子类型PROCESS_COMMUNICATION

身体描述

processname:name groupname:name pid:pid
data

SUPERVISOR_STATE_CHANGE 事件类型

supervisord 进程的状态改变时引发的事件类型。这种类型是抽象的,它永远不会被直接发送。订阅此事件类型将导致订阅者接收 SUPERVISOR_STATE_CHANGE 的所有子类型的事件通知。

NameSUPERVISOR_STATE_CHANGE

子类型EVENT

身体描述:N/A

SUPERVISOR_STATE_CHANGE_RUNNING 事件类型

表示 supervisord 已启动。

NameSUPERVISOR_STATE_CHANGE_RUNNING

子类型SUPERVISOR_STATE_CHANGE

身体描述:空字符串

SUPERVISOR_STATE_CHANGE_STOPPING 事件类型

表示 supervisord 正在停止。

NameSUPERVISOR_STATE_CHANGE_STOPPING

子类型SUPERVISOR_STATE_CHANGE

身体描述:空字符串

TICK 事件类型

可以订阅的事件类型,用于事件监听器每N秒接收“唤醒”通知。这个事件类型是抽象的,它永远不会被直接发送。订阅此事件类型将导致订阅者接收 TICK 的所有子类型的事件通知。

请注意,唯一可用的 TICK 事件是下面列出的事件。您不能订阅任意 TICK 间隔。如果您需要下面未提供的间隔,您可以订阅以下给出的较短间隔之一,并记录事件侦听器中运行之间的时间。

NameTICK

子类型EVENT

身体描述:N/A

TICK_5 事件类型

可以订阅的事件类型,用于事件监听器每5秒钟接收“唤醒”通知。

NameTICK_5

子类型TICK

身体描述

此事件类型是使用单个键设置的令牌:“when”,表示发送tick的时期。

when:1201063880

TICK_60 事件类型

可以订阅的事件类型,用于事件侦听器每60秒接收“唤醒”通知。

NameTICK_60

子类型TICK

身体描述

此事件类型是使用单个键设置的令牌:“when”,表示发送tick的时期。

when:1201063880

TICK_3600 事件类型

可以订阅的事件类型,用于事件侦听器每3600秒(1小时)接收“唤醒”通知。

NameTICK_3600

子类型TICK

身体描述

此事件类型是使用单个键设置的令牌:“when”,表示发送tick的时期。

when:1201063880

PROCESS_GROUP 事件类型

向Supervisor添加或从Supervisor中删除进程组时引发的事件类型。这种类型是抽象的,它永远不会被直接发送。订阅此事件类型将导致订阅者接收 PROCESS_GROUP 的所有子类型的事件通知。

NamePROCESS_GROUP

子类型EVENT

身体描述:N/A

PROCESS_GROUP_ADDED 事件类型

表示进程组已添加到Supervisor的配置。

NamePROCESS_GROUP_ADDED

子类型PROCESS_GROUP

身体描述:这个主体是一个只有一个groupname键/值的令牌集。

groupname:cat

PROCESS_GROUP_REMOVED 事件类型

表示已从Supervisor的配置中删除进程组。

NamePROCESS_GROUP_REMOVED

子类型PROCESS_GROUP

身体描述:这个主体是一个只有一个groupname键/值的令牌集。

groupname:cat