如何让Symfony Scheduler重新评估周期性任务并重启Worker?
解决Symfony调度任务修改后Worker不生效的问题
我将周期性任务定义在Doctrine实体中,用户可通过UI修改任务的执行频率,以及启用或禁用任务:
#[ORM\Entity(repositoryClass: ScheduledTaskRepository::class)] class ScheduledTask { // ... #[ORM\Column(length: 16)] public ?string $frequency = null; #[ORM\Column] public bool $enabled = true; public function toRecurringMessage(): RecurringMessage { if (null === $this->type || null === $this->frequency) { throw new \LogicException(static::class.' is not properly initialized.'); } return RecurringMessage::cron($this->frequency, new SyncEmployees()); } }
当前调度器在启动时从数据库加载所有已启用的任务,并将对应的周期性消息添加到任务队列,但存在明显局限:任务被禁用后Worker仍会继续执行;任务被启用后Worker无法识别,因为任务仅在调度器启动时加载。
问题
如何让由Supervisor管理的Worker进程重启,使其重新评估任务列表?当前Supervisor配置如下:
[program:messenger-scheduler] command=php /app/bin/console messenger:consume scheduler_default --time-limit=3600 user=root numprocs=1 startsecs=0 autostart=true autorestart=true startretries=10 process_name=%(program_name)s_%(process_num)02d
解决方案
方案1:手动执行Supervisor命令重启进程
直接通过Supervisor命令行工具重启指定的Worker进程:
# 重启所有messenger-scheduler进程 supervisorctl restart messenger-scheduler:* # 也可以分两步执行(效果一致) supervisorctl stop messenger-scheduler:* supervisorctl start messenger-scheduler:*
方案2:在业务逻辑中自动触发重启
如果需要在用户修改任务后自动触发Worker重启,可以在任务保存的逻辑中调用Supervisor命令。注意要确保PHP进程拥有执行supervisorctl的权限:
// 示例:在任务保存的Controller或Service中 use Symfony\Component\Process\Process; // 构建重启命令 $process = new Process(['supervisorctl', 'restart', 'messenger-scheduler:*']); $process->run(); // 检查命令执行结果 if (!$process->isSuccessful()) { // 记录错误日志或抛出异常 throw new \RuntimeException('Worker重启失败: ' . $process->getErrorOutput()); }
方案3:利用现有配置自动定时重启
你的Supervisor配置中已经设置了--time-limit=3600,这意味着Worker进程每1小时会自动退出,Supervisor会根据autorestart=true自动启动新进程,新进程会重新加载最新的任务列表。
如果觉得1小时的间隔太长,可以缩短时间,比如改为30分钟(1800秒):
command=php /app/bin/console messenger:consume scheduler_default --time-limit=1800
这种方式无需额外开发,适合对实时性要求不高的场景。
可选优化:无需重启的动态任务加载
如果不想依赖进程重启,可以修改调度器逻辑,取消对初始任务列表的缓存,每次评估任务时都从数据库重新加载:
#[AsSchedule] class Schedule implements ScheduleProviderInterface { public function __construct( private readonly CacheInterface $cache, private readonly ScheduledTaskRepository $tasks, ) { } #[\Override] public function getSchedule(): SymfonySchedule { // 不再缓存$this->schedule,每次都重新构建任务列表 return new SymfonySchedule() ->stateful($this->cache) ->processOnlyLastMissedRun(true) ->add( ...array_map( fn (ScheduledTask $t) => $t->toRecurringMessage()->withJitter(30), $this->tasks->allEnabled() ), ) ; } }
注意:这种方式会在每次调度时查询数据库,需要评估性能影响,适合任务数量较少的场景。
内容的提问来源于stack exchange,提问作者gremo
相关产品推荐
相关产品推荐

