如何在 PHP 中对 Unix 信号处理进行可靠的集成测试?
How to do reliable integration testing of Unix signal handling in PHP?
我正在编写一个 运行 在后台运行的服务器系统。用非常简单的术语来说,它有自己的脚本语言,这意味着一个进程可以用该语言编写成 运行 自己,或者它可以调用另一个进程,等等。我正在将这个系统从一个微不足道的 PHP cron-job,其中一次只允许一个实例到一组由 Supervisor 管理的 long-运行ning 进程。
考虑到这一点,我知道这些进程可以随时终止,无论是在开发过程中由我自己终止,还是由 Supervisord 在停止或重新启动 worker 的正常过程中终止。我想添加一些适当的信号处理以确保工作人员自行整理,并在适当的地方记录任务处于中断状态的位置。
我已经弄清楚如何使用 ticks 和 pcntl_signal()
启用信号处理,目前我的处理似乎工作正常。但是,我想对此进行测试以确保它是可靠的。我已经编写了一些早期的集成测试,但感觉并不那么可靠,主要是因为在开发过程中存在各种奇怪的竞争条件问题,这些问题很难确定。
我想要一些关于如何在 PHP 单元测试中发送 kill 信号的建议或指导,以提高对我的 sig 处理稳健性的信心。我目前的策略:
- 使用PHP单位
- 作为核心系统运行它创建各种日志文件,可用于监控何时终止任务
- 核心系统在后台使用单独的 PHP 脚本启动,在 PHP 单元测试中使用
system()
命令。我的命令类似于php script.php > $logFile 2>&1 &
,即将所有输出重定向到日志文件,然后将其推送到后台,以便测试方法可以监控它
- 后台脚本将它的 PID 写入一个文件,这将是要杀死的 PID
- 测试通过反复扫描并在扫描之间
usleep
ing 可靠地拾取它
- 测试然后通过扫描数据库等待特定状态,
usleep
扫描之间,并在准备就绪时发出 kill <pid>
- 然后等待信号处理程序启动并写入新的数据库状态,
usleep
再次调用以避免重击数据库
- 最后,它会在最大延迟时间后确定数据库是否处于正确状态,passes/fails 测试。
当然,有了这一切 waiting/checking,它感觉有点粗糙,并且对于各种竞争条件来说已经很成熟了。我目前的感觉是测试将在大约 2% 的时间内失败,但我已经有一天左右无法让测试失败了。我打算做一些浸入式测试,如果我从中遇到任何失败,我会 post 在这里。
我想知道我是否可以通过要求被测系统 kill
本身来简化它,这将删除两级等待检查(一个等待 PID,另一个等待数据库在 kill 命令之前进入正确的状态)†。发出 kill 后仍然会离开等待检查循环,但我可能会发现进行一次检查在实践中不是问题。
也就是说,我意识到我的整个方法可能是笨拙的,并且有更好的方法来做这种事情。有任何想法吗?目前我的想法只是增加我的等待超时,以防 PHPUnit 引入任何奇怪的延迟。我也会看看我是否可以得到一个失败案例来检查日志。
† 啊,遗憾的是它不会简化事情。我只是在一个我认为可靠的简单信号集成测试上尝试了这个,并且由于后台 system()
returns 立即,它仍然必须循环等待以识别正确的日志记录,然后为正确的post-杀结果。但是,它不再需要等待将 PID 写入临时文件,因此至少消除了一个循环。
正如我在问题中提到的,我尝试的第一个可靠性更改是将工作任务的能力注入 运行 kill
自身。在我的例子中,这是系统内置的,但读者可能会发现编写 child 测试 class 并更改其 DI 配置将是一种方便的方法。
这似乎大大提高了可靠性。原来,测试中有几个等待循环,测试必须在正确的时刻 运行 the kill
:
- 等待 child 的 PID 可用
- 等待 child 日志文件表明它已准备好终止
- 发出
kill
- 等待 child 日志文件正确指示信号处理程序 运行
问题可能出在 (2) - 如果这太短,那么 kill
有时可能会来得太晚,即使找到可靠的最长等待时间,如果 CPU 处于意外负载下,那么它可能仍然容易出现故障。
我现在已经编写了一个快速脚本来重复 运行 PHPUnit 测试,无论是 200 次迭代还是第一次失败,以先到者为准。这现在通过了 200 次迭代,所以暂时我会认为测试可靠性已经上升。但是,如果这种情况发生变化,我会在这里更新 - 也许 运行 高 nice
测试会触发失败。
其他答案仍然是最受欢迎的。
我正在编写一个 运行 在后台运行的服务器系统。用非常简单的术语来说,它有自己的脚本语言,这意味着一个进程可以用该语言编写成 运行 自己,或者它可以调用另一个进程,等等。我正在将这个系统从一个微不足道的 PHP cron-job,其中一次只允许一个实例到一组由 Supervisor 管理的 long-运行ning 进程。
考虑到这一点,我知道这些进程可以随时终止,无论是在开发过程中由我自己终止,还是由 Supervisord 在停止或重新启动 worker 的正常过程中终止。我想添加一些适当的信号处理以确保工作人员自行整理,并在适当的地方记录任务处于中断状态的位置。
我已经弄清楚如何使用 ticks 和 pcntl_signal()
启用信号处理,目前我的处理似乎工作正常。但是,我想对此进行测试以确保它是可靠的。我已经编写了一些早期的集成测试,但感觉并不那么可靠,主要是因为在开发过程中存在各种奇怪的竞争条件问题,这些问题很难确定。
我想要一些关于如何在 PHP 单元测试中发送 kill 信号的建议或指导,以提高对我的 sig 处理稳健性的信心。我目前的策略:
- 使用PHP单位
- 作为核心系统运行它创建各种日志文件,可用于监控何时终止任务
- 核心系统在后台使用单独的 PHP 脚本启动,在 PHP 单元测试中使用
system()
命令。我的命令类似于php script.php > $logFile 2>&1 &
,即将所有输出重定向到日志文件,然后将其推送到后台,以便测试方法可以监控它 - 后台脚本将它的 PID 写入一个文件,这将是要杀死的 PID
- 测试通过反复扫描并在扫描之间
usleep
ing 可靠地拾取它
- 测试然后通过扫描数据库等待特定状态,
usleep
扫描之间,并在准备就绪时发出kill <pid>
- 然后等待信号处理程序启动并写入新的数据库状态,
usleep
再次调用以避免重击数据库 - 最后,它会在最大延迟时间后确定数据库是否处于正确状态,passes/fails 测试。
当然,有了这一切 waiting/checking,它感觉有点粗糙,并且对于各种竞争条件来说已经很成熟了。我目前的感觉是测试将在大约 2% 的时间内失败,但我已经有一天左右无法让测试失败了。我打算做一些浸入式测试,如果我从中遇到任何失败,我会 post 在这里。
我想知道我是否可以通过要求被测系统 kill
本身来简化它,这将删除两级等待检查(一个等待 PID,另一个等待数据库在 kill 命令之前进入正确的状态)†。发出 kill 后仍然会离开等待检查循环,但我可能会发现进行一次检查在实践中不是问题。
也就是说,我意识到我的整个方法可能是笨拙的,并且有更好的方法来做这种事情。有任何想法吗?目前我的想法只是增加我的等待超时,以防 PHPUnit 引入任何奇怪的延迟。我也会看看我是否可以得到一个失败案例来检查日志。
† 啊,遗憾的是它不会简化事情。我只是在一个我认为可靠的简单信号集成测试上尝试了这个,并且由于后台 system()
returns 立即,它仍然必须循环等待以识别正确的日志记录,然后为正确的post-杀结果。但是,它不再需要等待将 PID 写入临时文件,因此至少消除了一个循环。
正如我在问题中提到的,我尝试的第一个可靠性更改是将工作任务的能力注入 运行 kill
自身。在我的例子中,这是系统内置的,但读者可能会发现编写 child 测试 class 并更改其 DI 配置将是一种方便的方法。
这似乎大大提高了可靠性。原来,测试中有几个等待循环,测试必须在正确的时刻 运行 the kill
:
- 等待 child 的 PID 可用
- 等待 child 日志文件表明它已准备好终止
- 发出
kill
- 等待 child 日志文件正确指示信号处理程序 运行
问题可能出在 (2) - 如果这太短,那么 kill
有时可能会来得太晚,即使找到可靠的最长等待时间,如果 CPU 处于意外负载下,那么它可能仍然容易出现故障。
我现在已经编写了一个快速脚本来重复 运行 PHPUnit 测试,无论是 200 次迭代还是第一次失败,以先到者为准。这现在通过了 200 次迭代,所以暂时我会认为测试可靠性已经上升。但是,如果这种情况发生变化,我会在这里更新 - 也许 运行 高 nice
测试会触发失败。
其他答案仍然是最受欢迎的。