本文目录导读:

我来详细介绍Symfony Scheduler组件的使用和配置。
什么是Symfony Scheduler
Symfony Scheduler是Symfony框架的任务调度组件,用于管理定时任务、周期性任务和一次性任务,它提供了比cron更灵活和强大的任务调度能力。
安装
composer require symfony/scheduler
基本配置
启用调度器
在 config/packages/scheduler.yaml 中配置:
# config/packages/scheduler.yaml
framework:
scheduler:
enabled: true
创建任务
创建任务类
// src/Scheduler/MyTask.php
namespace App\Scheduler;
use Symfony\Component\Scheduler\Attribute\AsCronTask;
use Symfony\Component\Scheduler\Attribute\AsPeriodicTask;
#[AsCronTask('*/5 * * * *')] // 每5分钟执行一次
#[AsPeriodicTask('10 minutes')] // 每10分钟执行一次
class MyTask
{
public function __invoke(): void
{
// 执行任务逻辑
$this->doSomething();
}
private function doSomething(): void
{
// 你的业务逻辑
file_put_contents('/tmp/scheduler.log',
sprintf("Task executed at %s\n", date('Y-m-d H:i:s')),
FILE_APPEND
);
}
}
高级用法
使用Schedule对象
// src/Scheduler/TaskProvider.php
namespace App\Scheduler;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
class TaskProvider implements ScheduleProviderInterface
{
public function getSchedule(): Schedule
{
$schedule = new Schedule();
// 添加周期性任务
$schedule->add(
RecurringMessage::every('5 minutes', new SendNewsletter())
);
// 添加Cron表达式任务
$schedule->add(
RecurringMessage::cron('0 0 * * *', new DailyReport())
);
// 添加一次性任务
$schedule->add(
RecurringMessage::at(new \DateTimeImmutable('2024-12-31 23:59:59'), new NewYearTask())
);
return $schedule;
}
}
任务类型定义
// src/Message/SendNewsletter.php
namespace App\Message;
class SendNewsletter
{
public function __construct(
private string $newsletterType,
private array $recipients = []
) {}
public function getNewsletterType(): string
{
return $this->newsletterType;
}
public function getRecipients(): array
{
return $this->recipients;
}
}
消息处理器
// src/MessageHandler/SendNewsletterHandler.php
namespace App\MessageHandler;
use App\Message\SendNewsletter;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
class SendNewsletterHandler
{
public function __invoke(SendNewsletter $message): void
{
// 处理邮件发送
$type = $message->getNewsletterType();
$recipients = $message->getRecipients();
// 发送邮件逻辑
foreach ($recipients as $recipient) {
// 发送邮件
}
}
}
使用Cron表达式
// 常用的Cron表达式
#[AsCronTask('* * * * *')] // 每分钟
#[AsCronTask('*/5 * * * *')] // 每5分钟
#[AsCronTask('0 * * * *')] // 每小时
#[AsCronTask('0 0 * * *')] // 每天午夜
#[AsCronTask('0 0 * * 0')] // 每周日
#[AsCronTask('0 0 1 * *')] // 每月1号
#[AsCronTask('0 */2 * * *')] // 每2小时
#[AsCronTask('0 0 * * 1-5')] // 工作日午夜
使用周期性时间间隔
// 使用人类可读的时间格式
#[AsPeriodicTask('5 minutes')] // 每5分钟
#[AsPeriodicTask('1 hour')] // 每小时
#[AsPeriodicTask('1 day')] // 每天
#[AsPeriodicTask('2 weeks')] // 每2周
#[AsPeriodicTask('1 month')] // 每月
// 使用DateInterval
#[AsPeriodicTask('P1D')] // 每天
#[AsPeriodicTask('PT1H')] // 每小时
#[AsPeriodicTask('PT30M')] // 每30分钟
运行调度器
启动调度器消费者
# 在后台运行调度器 php bin/console scheduler:consume # 指定调度器名称 php bin/console scheduler:consume default # 使用进程管理器(Supervisor)配置
Supervisor配置
# /etc/supervisor/conf.d/symfony-scheduler.conf [program:symfony-scheduler] command=php /path/to/your/project/bin/console scheduler:consume --time-limit=3600 directory=/path/to/your/project autostart=true autorestart=true stderr_logfile=/var/log/symfony/scheduler.err.log stdout_logfile=/var/log/symfony/scheduler.out.log numprocs=2 process_name=%(program_name)s_%(process_num)02d
完整示例
邮件通知任务
// src/Scheduler/EmailNotificationTask.php
namespace App\Scheduler;
use Symfony\Component\Scheduler\Attribute\AsPeriodicTask;
use Psr\Log\LoggerInterface;
#[AsPeriodicTask('1 hour')]
class EmailNotificationTask
{
public function __construct(
private LoggerInterface $logger
) {}
public function __invoke(): void
{
try {
// 检查待发送邮件
$pendingEmails = $this->getPendingEmails();
foreach ($pendingEmails as $email) {
$this->sendEmail($email);
$this->markAsSent($email);
}
$this->logger->info('Email notification task completed', [
'count' => count($pendingEmails)
]);
} catch (\Exception $e) {
$this->logger->error('Email notification failed', [
'error' => $e->getMessage()
]);
}
}
private function getPendingEmails(): array
{
// 从数据库获取待发送邮件
return [];
}
private function sendEmail(array $email): void
{
// 发送邮件逻辑
}
private function markAsSent(array $email): void
{
// 标记为已发送
}
}
数据清理任务
// src/Scheduler/CleanupTask.php
namespace App\Scheduler;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Scheduler\Attribute\AsCronTask;
#[AsCronTask('0 3 * * *')] // 每天凌晨3点执行
class CleanupTask
{
public function __construct(
private EntityManagerInterface $entityManager
) {}
public function __invoke(): void
{
// 清理30天前的日志
$this->cleanOldLogs();
// 清理过期会话
$this->cleanExpiredSessions();
// 清理临时文件
$this->cleanTempFiles();
}
private function cleanOldLogs(): void
{
$date = new \DateTime('-30 days');
$this->entityManager
->createQuery('DELETE FROM App\Entity\Log l WHERE l.createdAt < :date')
->setParameter('date', $date)
->execute();
}
private function cleanExpiredSessions(): void
{
// 清理过期session
}
private function cleanTempFiles(): void
{
// 清理临时文件
$tempDir = sys_get_temp_dir() . '/app';
if (is_dir($tempDir)) {
array_map('unlink', glob("$tempDir/*"));
}
}
}
最佳实践
错误处理
#[AsPeriodicTask('5 minutes')]
class RobustTask
{
public function __invoke(): void
{
try {
// 任务逻辑
} catch (\Exception $e) {
// 记录日志
$this->logger->error('Task failed', [
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
// 发送告警通知
$this->notifyAdmin($e);
// 重新抛出异常(可选)
throw $e;
}
}
}
任务锁
use Symfony\Component\Lock\LockFactory;
#[AsPeriodicTask('10 minutes')]
class LockedTask
{
public function __construct(
private LockFactory $lockFactory
) {}
public function __invoke(): void
{
$lock = $this->lockFactory->createLock('task-lock');
if (!$lock->acquire()) {
// 任务已在运行,跳过
return;
}
try {
// 执行任务
$this->doWork();
} finally {
$lock->release();
}
}
}
监控和日志
#[AsPeriodicTask('1 hour')]
class MonitoredTask
{
public function __invoke(): void
{
$startTime = microtime(true);
// 执行任务
$this->doWork();
$duration = microtime(true) - $startTime;
// 记录性能指标
$this->logger->info('Task completed', [
'duration' => $duration,
'memory_usage' => memory_get_peak_usage(true)
]);
}
}
注意事项
- 确保只有一个实例运行:使用锁机制防止并发执行
- 监控执行时间:避免任务重叠执行
- 处理异常:确保异常不会导致整个调度器崩溃
- 记录日志:便于问题排查和性能监控
- 使用Supervisor:保证调度器持续运行
- 考虑分布式环境:在多服务器环境中需要使用分布式锁
Symfony Scheduler为PHP项目提供了强大、灵活的任务调度能力,相比传统cron更加易于维护和扩展。