Skip to content

定时任务

MineShop 使用 Hyperf 的 Crontab 组件实现定时任务,支持秒级调度和分布式锁。

🎯 任务列表

任务执行周期说明
SeckillActivityStatusCrontab每10分钟秒杀活动状态推进
GroupBuyExpireCrontab每5分钟团购过期处理
OrderAutoCloseCrontab每分钟订单自动关闭
StatisticsAggregateCrontab每小时统计数据聚合
GeoRegionSyncCrontab每天凌晨地区数据同步

📦 任务实现

秒杀状态推进

php
// SeckillActivityStatusCrontab.php
#[Crontab(
    name: 'seckill-activity-status',
    rule: '* */10 * * * *',  // 每10分钟
    callback: 'execute',
    memo: '秒杀活动状态自动推进'
)]
class SeckillActivityStatusCrontab
{
    public function execute(): void
    {
        // 1. 处理待开始的场次(30分钟内)
        $this->processPendingSessions();
        
        // 2. 处理已过期的场次
        $this->processExpiredSessions();
        
        // 3. 联动激活活动
        $this->processActivityStart();
        
        // 4. 联动结束活动
        $this->processActivityEnd();
    }
}

待开始场次处理

php
private function processPendingSessions(): void
{
    // 查找30分钟内即将开始的场次
    $sessions = $this->sessionRepository->findPendingSessionsWithinMinutes(30);
    
    foreach ($sessions as $session) {
        try {
            $startTime = Carbon::parse($session->start_time);
            
            if ($startTime->lte(Carbon::now())) {
                // 已到开始时间,立即激活并预热缓存
                $this->sessionService->start($session->id);
                $this->cacheService->warmSession($session->id);
            } else {
                // 未到开始时间,推送延迟任务
                $delaySeconds = (int) $startTime->diffInSeconds(Carbon::now());
                $this->driverFactory->get('default')->push(
                    new SeckillSessionStartJob($session->id),
                    $delaySeconds
                );
            }
        } catch (\Throwable $e) {
            $this->logger->error('处理待开始场次失败', [
                'id' => $session->id,
                'error' => $e->getMessage()
            ]);
        }
    }
}

过期场次处理

php
private function processExpiredSessions(): void
{
    foreach ($this->sessionRepository->findActiveExpiredSessions() as $session) {
        try {
            // 跳过已取消或已售罄的场次
            if (in_array($session->status, ['cancelled', 'sold_out'])) {
                continue;
            }
            
            $this->sessionService->end($session->id);
            $this->logger->info('场次已结束', ['id' => $session->id]);
        } catch (\Throwable $e) {
            $this->logger->error('处理过期场次失败', [
                'id' => $session->id,
                'error' => $e->getMessage()
            ]);
        }
    }
}

🔧 Crontab 规则

*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    │
│    │    │    │    │    └── 星期 (0-7, 0和7都是周日)
│    │    │    │    └─────── 月份 (1-12)
│    │    │    └──────────── 日期 (1-31)
│    │    └───────────────── 小时 (0-23)
│    └────────────────────── 分钟 (0-59)
└─────────────────────────── 秒 (0-59, Hyperf 扩展)

常用示例:

  • * * * * * * - 每秒执行
  • 0 * * * * * - 每分钟执行
  • 0 */10 * * * * - 每10分钟执行
  • 0 0 * * * * - 每小时执行
  • 0 0 0 * * * - 每天凌晨执行

🔒 分布式锁

多实例部署时,使用 Redis 锁防止重复执行:

php
#[Crontab(
    name: 'my-task',
    rule: '0 * * * * *',
    callback: 'execute',
    singleton: true,  // 启用单例模式
    onOneServer: true // 只在一台服务器执行
)]

⚠️ 注意事项

  1. 执行时间: 任务执行时间不要超过调度间隔
  2. 异常处理: 捕获异常避免影响其他任务
  3. 日志记录: 记录关键操作便于排查问题
  4. 监控告警: 配置任务执行失败告警

📚 相关文档

基于 Apache-2.0 许可发布 | 感谢 MineAdmin 提供的优秀基础框架