Perl Advent Calendar 2010-12-07

snaked 任务调度器

by cnhackTNT

我们经常会碰到需要定时执行某些程序的需求,这种时候一般脑中最先闪现的便是 cron 或是 Windows 计划任务, 很少有人知道 snaked 这个模块或着说工具。

snakedPetya Kohts 用 Perl 开发的一个任务调度程序,在 Linux 和 FreeBSD 上都经过了大量的测试,貌似俄罗斯第一的 Yandex.ru 内部也有在使用它。

它能完成和 cron 一样的功能,也就是定时执行程序。但它还有一些 cron 所没有的特性,如:

1. 可以限制任务运行所耗费的时间(包括单个任务或总任务耗时)

2. 能处理任务间的依赖关系

3. cron 最小计时单位为分钟,而 snaked 最小单位为秒

4. 任务启动时随机延时功能

更重要的一点,它是由 Perl 编写的,几乎在兼容 POSIX 标准的系统上均可以运行。

下面让我们来安装它吧。

安装 snaked

我们可以用前面提到过的 cpanm 来安装该模块,非常简单:

$ cpanm snaked Proc::ProcessTable

Proc::ProcessTable 是它所依赖的模块之一。

安装完成后,一般会在 perl 所在的 bin/ 目录下生成 snaked 可执行程序,通常这个 bin/ 目录应该已经在你当前的 PATH 环境变量里了,所以直接运行 snaked 就可以看到它输出的帮助信息。

snaked 配置目录结构

如果不指定,snaked 的默认配置目录指向 /etc/snaked,一般来说我们会自定义配置目录的位置。举例来说,其结构为:

$ tree demo_snaked/

demo_snaked/
├── admin_email
├── jobs
│   ├── job_5min
│   │   ├── cmd
│   │   ├── conflicts
│   │   └── execution_interval
│   └── job_1min
│       ├── cmd
│       └── execution_interval
├── log
└── pidfile

上面的 demo_snaked 是一个 snaked 配置目录,admin_email, log, pidfile 各自都是文本文件,其内容都是一行,分别代表这几个配置项:管理员的电子邮件地址,日志文件路径,pid 文件路径。

分别打开看一下:

$ cd demo_snaked/

$ cat admin_email
webmaster@someweb.com

$ cat log
/home/cnhacktnt/demo_snaked/snaked.log

$ cat pidfile
/home/cnhacktnt/demo_snaked/snaked.pid

当然还有一些其他的配置项,有些配置项也有默认的值,这个我们可以参看 snaked 的文档

jobs 任务目录

jobs 目录是重点,它的名字是固定的,它里面存放有一个或多个目录,每个目录都代表一个单独的任务,目录的名字既为任务的唯一 id,比如说上面的例子中就包含两个任务:job_5minjob_1min,这个名字你可以随意取,不重复即可(一般文件系统也不会允许同一级有目录重名)。

每个任务所在的目录,其结构是类似的。

cmd 文件

cmd 文件即该任务所需要执行的程序,它可以是 Shell 脚本或其他可执行应用程序,注意权限。

execution_interval 文件

execution_interval 文件指定了任务的执行间隔,以秒为单位,不妨看看例子中该文件的内容:

$ cd job_5min/

$ cat execution_interval
300

这里的 300 代表该任务每300秒执行一次(即每300秒执行该目录中的 cmd 文件一次)。

当然我们还可以不用 execution_interval,换用 execution_schedule 文件来以 crontab 的格式指定任务运行计划:

$ ls job_5min/
cmd  execution_schedule  conflicts

$ cat execution_schedule
30 2 * * *

以上代表 job_5min 中的 cmd 会在每天的 2:30 分运行。

conflicts 文件

conflicts 文件用来指定依赖关系,它的内容是以空格或换行分隔的任务id,意思是直到这些依赖任务执行完毕,本任务才执行。如果没有这个 conflicts 文件,那么一到计划时间,本任务就执行。

这个可以用来做一些简单的依赖管理。

看看上面例子中该文件的内容:

$ cat job_5min/conflicts
job_1min

$ cat job_1min/conflicts
job_5min

这代表本任务 job_5min 会等 job_1min 任务运行完,它再运行。

而轮到任务 job_1min 运行时,它也会检查看看 job_5min 是否在运行,如果 job_5min 当前不在运行,则 job_1min 运行。

其他配置文件

我们还可以在指定任务的目录下写其他一些配置文件,如:

- execution_timeout 文件用来指定任务超时时间,以秒为单位

- start_random_sleep 文件用来指定任务启动时随机延迟的时间,以秒为单位。当我们有很多任务在时间点上有重合时,可以指定随机启动延时,以避免大量任务同时启动造成系统负载增高。

- notification_interval 文件用来指定当任务运行出错时,在多少秒时间内不重复发送任务出错报告邮件。这个在频繁运行的程序出错时,可以让管理员的邮箱不被错误报告塞满。

关于其他配置文件和选项,请参看 snaked 的文档

运行 snaked 守护进程

当我们准备好了任务配置文件后,需要运行 snaked 来使任务生效:

下面是以守护进程模式运行:

$ pwd
/home/cnhacktnt/demo_snaked

$ snaked --daemon --cfg /home/cnhacktnt/demo_snaked

检查一下当前状态:

$ snaked --status --cfg /home/cnhacktnt/demo_snaked
snaked is running as pid 19326. command line [/home/cnhacktnt/perl512/bin/perl5.12.1 /home/cnhacktnt/perl512/bin/snaked --daemon --cfg /home/cnhacktnt/demo_snaked/ ]

$ snaked --show-jobs --cfg /home/cnhacktnt/demo_snaked
    global options
      admin_email: webmaster@someweb.com
      log: /home/cnhacktnt/demo_snaked/snaked.log
      pidfile: /home/cnhacktnt/demo_snaked/snaked.pid
    configured jobs:
      job_5min
        cmd: /home/cnhacktnt/demo_snaked/jobs/job_5min/cmd
        conflicts: job_1min
        execution_interval: 300
        execution_timeout: 0
        kill_timeout: 60
        mtime: 1291645265
      job_1min
        cmd: /home/cnhacktnt/demo_snaked/jobs/job_1min/cmd
        conflicts: job_5min
        execution_interval: 60
        execution_timeout: 0
        kill_timeout: 60
        mtime: 1291644889

我们还可以以调试模式运行 snaked

$ snaked --debug --cfg /home/cnhacktnt/demo_snaked

当我们修改了单个任务目录中的配置文件时,不需要重启 snaked,它会检测到改动,重新加载任务。

如果我们改动的是 snaked 的配置文件,如:admin_email, log 等时,需要告诉 snaked 重新加载配置:

$ snaked  --configure --cfg /home/cnhacktnt/demo_snaked

针对多个不同的 snaked 配置目录,我们可以通过 --cfg 参数来灵活运行多个 snaked 守护进程。

应用举例

假设目前有这么个需求(先不管它合理性和最优方案什么的):

任务A:每隔1分钟去 Flickr 抓取最新照片缩略图(100x67 像素大小的)及相关页面连接和图片说明,并存储到本地 data 目录中。

任务B:每隔5分钟查看本地 data 目录,如果目录中有超过30张缩略图则生成一个图片列表网页,并将这些图片及网页通过 scp 复制到远程的多台服务器中,在传输前先清空远程服务器上目录中的旧文件。

要求:

1. 任务B必须在任务A完成后才能运行,任务B完成后清空 data 目录。

2. 任务A必须等待任务B运行完毕后才可以运行,以免和任务B清空 data 目录的过程发生冲突。

这样的任务有点类似简单 CDN 发布的流程,任务本身不复杂,用 snaked 来做任务调度和依赖管理,用 Perl 写具体任务的 cmd 脚本很容易搞定。

其中将抓取的图片和生成的网页通过 scp 传输到多台远程服务器的过程,可以用去年提到过的 SSH::Batch 模块所带的 tonodes 命令来完成。

完整的代码请参看我的Github仓库,生成的页面效果参看 knocks.me/demo_snaked/

有问题可以 @cnhacktnt 联系我(你懂的)

谢谢!

View Source (POD)