ThinkPHP项目任务调度与Cron

wen PHP项目 5

本文目录导读:

ThinkPHP项目任务调度与Cron

  1. ThinkPHP自带的多应用/命令行模式(推荐)
  2. 使用Cron直接调用
  3. 使用Laravel式调度器(ThinkPHP扩展)
  4. 使用系统调度服务(Linux systemd)
  5. 在Windows环境下使用任务计划程序
  6. 高级:数据库驱动调度
  7. 注意事项与最佳实践
  8. 总结与选型建议

在ThinkPHP项目中实现任务调度(Task Scheduling)与Cron,有多种方案可以选择,具体取决于你的项目需求和运行环境。

以下是几种主流且实用的实现方式:

ThinkPHP自带的多应用/命令行模式(推荐)

这是最规范的实现方式,适合复杂业务逻辑。

1 创建自定义命令

app/command/ 目录下创建命令类(如果目录不存在则创建):

<?php
// app/command/TestTask.php
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Db;
class TestTask extends Command
{
    protected function configure()
    {
        $this->setName('task:test')
            ->setDescription('测试定时任务');
    }
    protected function execute(Input $input, Output $output)
    {
        // 你的业务逻辑代码
        $output->writeln('任务开始执行: ' . date('Y-m-d H:i:s'));
        // 示例:清理过期日志
        Db::name('logs')->where('create_time', '<', time() - 86400*7)->delete();
        $output->writeln('任务执行完成');
    }
}

2 注册命令

config/console.php 中注册你的命令:

// config/console.php
return [
    'commands' => [
        \app\command\TestTask::class,
    ],
];

3 测试运行

# 在项目根目录执行
php think task:test

使用Cron直接调用

如果你的服务器环境是Linux/macOS,这是最简单直接的方式。

1 编辑Cron配置

crontab -e

2 添加Cron规则

# 每天凌晨2点执行
0 2 * * * cd /path/to/your/project && php think task:test >> storage/logs/cron.log 2>&1
# 每5分钟执行一次
*/5 * * * * cd /path/to/your/project && php think task:test
# 每小时的第一分钟执行
1 * * * * cd /path/to/your/project && php think task:test
# 每天上午9点30分执行
30 9 * * * cd /path/to/your/project && php think task:test

使用Laravel式调度器(ThinkPHP扩展)

如果你喜欢Laravel的Scheduler方式,可以安装第三方扩展。

1 安装topthink/think-scheduler

composer require topthink/think-scheduler

2 配置调度器

config/scheduler.php 配置你的任务:

<?php
// config/scheduler.php
return [
    // 调度任务定义
    'tasks' => [
        [
            'schedule' => '0 2 * * *',  // cron表达式
            'task' => \app\command\TestTask::class,
        ],
        [
            'schedule' => '*/5 * * * *',
            'command' => 'git pull',      // 也可以执行系统命令
        ],
    ],
];

3 然后在Cron中添加一行:

* * * * * cd /path/to/project && php think schedule:run >> storage/logs/schedule.log 2>&1

使用系统调度服务(Linux systemd)

对于运行在稳定服务器上的应用,可以使用systemd进行管理。

1 创建systemd service

# /etc/systemd/system/think-task.service
[Unit]
Description=ThinkPHP Task Runner
After=network.target
[Service]
Type=oneshot
User=www-data
WorkingDirectory=/var/www/your-project
ExecStart=/usr/bin/php think task:test

2 创建timer

# /etc/systemd/system/think-task.timer
[Unit]
Description=Run ThinkPHP task every minute
[Timer]
OnCalendar=*-*-* * *:00:00
Persistent=true
[Install]
WantedBy=timers.target

3 启用定时器

systemctl daemon-reload
systemctl enable think-task.timer
systemctl start think-task.timer

在Windows环境下使用任务计划程序

如果这是Windows环境:

1 创建批处理文件

@echo off
cd /d C:\path\to\your\project
php think task:test

2 添加任务计划

  • 打开“任务计划程序”
  • 创建基本任务
  • 设置触发器(每天/每周等)
  • 设置操作为“启动程序”
  • 选择刚创建的.bat文件

高级:数据库驱动调度

对于多台服务器或需要动态调度的场景,可以使用数据库驱动的方式:

1 创建任务表

CREATE TABLE `task_schedules` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `task_name` varchar(100) NOT NULL,
  `cron_expression` varchar(100) NOT NULL,
  `last_run` datetime DEFAULT NULL,
  `status` tinyint(1) DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

2 统一调度入口

<?php
// app/command/Scheduler.php
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Db;
class Scheduler extends Command
{
    protected function configure()
    {
        $this->setName('schedule:run')
            ->setDescription('运行所有的定时任务');
    }
    protected function execute(Input $input, Output $output)
    {
        $tasks = Db::name('task_schedules')
            ->where('status', 1)
            ->select();
        foreach ($tasks as $task) {
            if ($this->shouldRun($task)) {
                // 执行任务
                $this->runTask($task);
                // 更新最后运行时间
                Db::name('task_schedules')
                    ->where('id', $task['id'])
                    ->update(['last_run' => date('Y-m-d H:i:s')]);
                $output->writeln("执行任务: " . $task['task_name']);
            }
        }
    }
    private function shouldRun($task)
    {
        // 解析cron表达式并判断是否应该执行
        // ... 这里需要实现cron表达式的解析逻辑
        $lastRun = strtotime($task['last_run']);
        $now = time();
        // 简单示例:每分钟执行一次
        return ($now - $lastRun) >= 60;
    }
    private function runTask($task)
    {
        // 根据task_name分派到具体的执行逻辑
        switch ($task['task_name']) {
            case 'send_email':
                // 发送邮件逻辑
                break;
            // ... 其他任务
        }
    }
}

注意事项与最佳实践

1 日志记录

protected function execute(Input $input, Output $output)
{
    try {
        // 业务逻辑
        Log::info('任务执行成功: ' . date('Y-m-d H:i:s'));
    } catch (\Exception $e) {
        Log::error('任务执行失败: ' . $e->getMessage());
    }
    // 或通过输出重定向到日志文件
    $output->writeln('Task completed at: ' . date('Y-m-d H:i:s'));
}

2 避免任务重叠

// 使用文件锁防止任务重复执行
if (!file_exists('/tmp/task.lock')) {
    file_put_contents('/tmp/task.lock', time());
    try {
        // 业务逻辑
    } finally {
        unlink('/tmp/task.lock');
    }
}

3 环境变量区分

// 在任务中判断环境
if (app()->isDebug()) {
    // 开发环境
} else {
    // 生产环境
}

总结与选型建议

方案 适用场景 优点 缺点
ThinkPHP命令 + Cron 大多数项目 简单、灵活 需要手动维护Cron
ThinkPHP Scheduler扩展 Laravel迁移项目 语法熟悉 依赖第三方库
systemd定时器 Linux服务器 稳定、可监控 学习成本稍高
数据库驱动调度 多服务器/动态调度 可动态管理 实现复杂

推荐组合

  • 使用 ThinkPHP命令 + Linux Cron 作为基础方案
  • 配合 日志系统 + 监控报警 保证任务可靠性
  • 使用 队列(如think-queue)处理耗时任务

这样既能保持代码的清晰和可维护性,又能充分利用操作系统的稳定性。

抱歉,评论功能暂时关闭!