15: 任务

Phlo 内置了跨应用的任务运行器。每分钟会触发一次 tasks::run,这是一个系统 cron 条目或 Phlo Daemontasks 资源会根据 %app->tasks 进行声明式匹配。您的应用中没有 cron 语法,也没有外部调度程序。

15.1: 设置

三个步骤。

1. 在 data/app.json 中激活资源

{
	"resources": [..., "tasks"]
}

2. 描述您在 app.phlo 中的任务:

prop tasks => arr(
	cleanup: arr(do: 'account::cleanup', every: '5 minutes'),
	poll:    arr(do: fn() => external::pull(), every: 'minute'),
	backup:  arr(do: 'backup::run', daily: '03:00'),
	report:  arr(do: 'report::weekly', weekly: 'monday 09:00'),
)

3. 每分钟触发 tasks::run 使用 Phlo Daemon,在其主机映射中列出应用程序,守护进程会为您运行 tasks::run,完全不需要 cron 行:

require('./phlo-daemon.js')(3001, php, { '<host>': { app: '<app>/www/app.php', build: false } })

没有守护进程,系统的 cron 条目也能完成相同的任务:

* * * * * php-zts <app>/www/app.php tasks::run

将其放置在 /etc/cron.d/example-tasks(系统,6 个字段包括用户)或通过 crontab -u <user>(每个用户,5 个字段)。无论哪种方式,调度本身仍然保留在 %app->tasks 中;触发器仅提供分钟的滴答声。

15.2: 日程安排

每个任务选择一个调度键:

格式 示例
every: PHP 可读的持续时间字符串 'minute''5 minutes''2 hours''1 day'
daily: 'HH:MM' '03:00'
weekly: '<weekday> HH:MM' 'monday 09:00'

every: 'minute'(没有前导数字)在内部变为'1 minute'。通过 strtotime("+$every", 0) 进行解析。

15.3: 可调用的 (`do:`)

do: 字段接受三种形式:

类型 示例 被调用为
Closure fn() => external::pull() 直接
'Class::method' 'account::cleanup' account::cleanup()
资源名称 'backup' phlo('backup')

与正常请求不同,任务在 HTTP 生命周期之外运行:没有 %req,没有 %session。编写任务时请确保其自包含。

15.4: 磁盘上的状态 (`data/tasks/`)

tasks::run 会自动创建 data/tasks/ 并用三个文件保护每个任务:

文件 内容 何时
<name>.last 原始 Unix 时间戳 每次成功运行时,用于到期检查
<name>.json {schedule, return} 用于 Control Center 每次成功运行时
<name>.lock 空(mtime 计数) 在运行期间,TTL 1 小时

锁定防止慢任务自我重叠。TTL 故意设置为 1 小时:失败的任务会被停放,直到锁定过期;其他任务会照常运行。

15.5: 错误流程

tasks::run 中没有 try/catch。一个 Throwable 会冒泡到 Phlo 的框架异常处理程序,并写入 data/errors.json,就像构建错误一样。Phlo Control Center 在任务选项卡中显示它们。

15.6: Phlo 控制中心

Phlo 控制中心自动检测 data/tasks/

15.7: 示例

prop tasks => arr(
	heartbeat: arr(do: 'app::heartbeat', every: 'minute'),
)

static heartbeat => file_put_contents(data.'heartbeat.log', date('Y-m-d H:i:s').' tasks::run fired'.lf, FILE_APPEND | LOCK_EX)

Cron 条目:

* * * * * php-zts <app>/www/app.php tasks::run

每分钟调用一次 tasks::run,检查 heartbeat 是否为 every: 'minute'lastRun < 60s 之前,然后运行 app::heartbeat()。文件 data/tasks/heartbeat.last.json.lock 会被更新;在任务运行期间,中间的滴答会跳过该任务。

15.8: 不使用 cron 的调度

如果 Phlo Daemon 正在运行,它会每分钟为您运行 tasks::run,因此您不需要每个应用的 cron 条目。守护进程的主机映射中的每个应用都会收到每分钟的时间戳:

require('./phlo-daemon.js')(3001, '/usr/bin/php-zts', {
	'example.com': { app: '/var/www/example/www/app.php', build: false },
})

守护进程每分钟执行一次 tasks::run;你的 %app->tasks 调度(每个任务上的 every:)决定了实际触发的内容,正如 cron tick 一样。其他一切都是相同的:相同的 %app->tasks 匹配,相同的到期检查,相同的 data/tasks/ 状态。对于没有守护进程的主机,cron 仍然是后备选项;选择一个触发器,而不是两个。

我们使用必要的cookie来使该网站正常工作。在您的许可下,我们还使用分析工具来改善网站。