【问题标题】:Watchdog-like support (bash perl or python)类似看门狗的支持(bash perl 或 python)
【发布时间】:2015-01-26 04:40:32
【问题描述】:

我有一个 bash 脚本(我正在转换为 perl),它在无限循环 (while true; do) 中运行,以轮询我们网络上的设备并将它们的响应记录到文本文件中。在 (while) 循环的每次迭代中,该设备的文本文件都会附加其最新信息。

我想让这个脚本始终运行——如果它挂起、崩溃或不再写入适当的文本文件——应该重新启动它。

按照this StackOverflow 问题中发布的建议,我可以编写以下 bash 脚本:

until myserver; do
  echo "Server 'myserver' crashed with exit code $?.  Respawning.." >&2
  sleep 1
done

其中myserver 是轮询程序。这将解释脚本意外崩溃或挂起的问题,假设在这些情况下发出了非零退出代码。但是,如果脚本没有完全失败/退出,但失败的方式只是阻止它写入文本文件——在这种情况下,我也想重新启动脚本。这就是类似看门狗的脚本的用武之地。我可以使用 Python 的 watchdog 并编写一个脚本,该脚本使用 Observer 库来监视正在生成的文本文件 like in this example。我会触发停滞的文本文件来为 python 脚本发出非零退出,然后按如下方式扩充上述 bash 脚本:

until [myserver -o pythonMon]; do
  echo "Server 'myserver' crashed with exit code $?.  Respawning.." >&2
  sleep 1
done

其中pythonMon 是监控文本文件是否正确更新的python 脚本。不幸的是,这种方法需要三个脚本(主轮询脚本和两个监控脚本);这有点杂乱无章。我正在寻找优化/简化这种方法。有什么建议吗?理想情况下,我有一个脚本(至少一个监控脚本)来保持轮询脚本运行而不是两个。有没有办法将文件监控直接添加到 bash 或 perl 代码中? 这是在 64 位 CentOS 6.5 上运行的

【问题讨论】:

  • 我认为投票之间存在某种暂停。如何从程序中取出循环,以便它只询问硬件一次,记录结果并退出。然后您可以将其作为cron 作业运行,该作业将以相同的频率运行,并且不会出现重新启动的问题。如果轮询失败,会发生的只是数据序列中缺少一个条目。
  • @Borodin 有一个暂停,但轮询每 10 秒发生一次,因此 cron 作业不起作用,因为它只允许脚本每分钟执行一次(需要 10 秒) .
  • 那么你的 Perl 程序应该每十秒 fork 一个子 Perl 进程并让它轮询。父进程可以kill 并在生成一个新子进程之前收获最新的子进程,尽管您需要重新启动 “如果 [进程] 不再写入适当的文本文件”令人担忧。您在这里想象什么样的错误,如何对其进行测试?我会用一些示例代码写一个答案,但我目前使用的是平板电脑,无法测试任何东西。
  • @Borodin 谢谢。我不太熟悉在 perl 中分叉子进程,但会查找它。至于没有正确写入文件的错误,我还不确定根本原因,但已经看到轮询 bash 文件的 PID 仍然存在,但输出文本文件不再更新的情况。这导致我监控这些文本文件的写入,如果它们停滞不前则重新启动。我假设它可能是网络中的一个小问题或一个失败的轮询设备,并且由于某种原因停止了轮询脚本。这有意义吗?

标签: python bash perl watchdog


【解决方案1】:

我正在做一些类似的事情来监控一堆设备。不过,这在一定程度上取决于轮询频率 - 我通过 cron 以 3m 间隔产卵。

请记住,10 秒的样本可能非常密集,并且可能并不总是必要的 - 这确实取决于您的目标。

不管怎样,这项工作的工具是 Parallel::ForkManager。

#!/usr/bin/perl

use strict;
use warnings;

use Parallel::ForkManager;

my @targets = qw( server1 server2 );

my %test_list = { 'fetch_cpu' => \&fetch_cpu_stats, };


sub fetch_cpu_stats {
    my ($target) = @_;
    ## do something to $target;
    open( my $ssh_results, "-|", "ssh -n $target uptime" )
        or die $!;
    while (<$ssh_results>) {
        print;
    }
}

my $manager = Parallel::ForkManager->new(10);

while (1) {
    foreach my $test ( keys %test_list ) {
        foreach my $target (@targets) {
            $manager->start and next;
            print "$$ starting $test\n";
            &{$test_list{$test}}($target);
            $manager -> finish;
        }
    }
    sleep 10;
}

这将产生多达 10 个并发“测试”,并每 10 秒重新运行一次。可能值得某种“锁定”过程(使用flock)以方便使用 cron 检查您的“守护程序”脚本是否仍在运行。

应该是这样的:

open ( my $self, "<", $0 ) or die $!;
flock ( $self, 2 | 4 ) or die "$0 already running"; 

然后您可以每隔一段时间在 cron 中触发它,如果它因某种原因死亡,它会自行重新启动。

但无论如何 - 您可以让多个子例程(例如您的测试脚本)全部自主生成(并且为了奖励积分 - 它们将并行运行)。

【讨论】:

  • 谢谢。我会试一试。这可能正是我需要的。正如上面提到的@Borodin,这并不能完全解释我描述的“写入”失败,但正如我认为你的评论逃避的那样,我可能能够运行另一个测试输出文件是否被正确附加的子例程。我会更多地解决这个问题,并让你知道这是否解决了我的问题。再次感谢您。
  • 我在第 30 行 &amp;{$test_list{$test}}($target); 中对 sub fetch_cpu_stats 的取消引用调用遇到了一些麻烦。我收到以下错误:“不能使用未定义的值作为子例程引用”。我不确定问题出在哪里。语法对我来说是正确的,$test 似乎得到了适当的 coderef。但是,如果我直接调用 &amp;fetch_cpu_stats 而不是通过 %test_list 哈希取消引用,它似乎可以正常工作。对此有什么想法吗?同样,我很生疏,可能在这里遗漏了一些明显的东西。
  • 这应该可行,但作为替代方案 - $test_list{$test} -&gt; ($target); 应该做类似的事情。
  • 是的,我也尝试过,但得到了相同的结果。哦,好吧,那是杂草,我会解决它。感谢您对整体问题的帮助。我将探索一些选项来验证日志文件是否也在定期写入,也许是一个与 cronjob 配对的子例程,以验证该脚本是否正在运行。谢谢。
  • 可能是代码中的某个地方存在语法错误,因此可能值得再发一篇文章。
猜你喜欢
  • 1970-01-01
  • 2016-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多