You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 21:44:50