开发文档

鲜花交易系统与鲜花交易平台(PHP / ThinkPHP 版)的研发记录:功能上线、架构演进与实施经验,按发布日期持续更新。

交易平台的定时任务怎么做:从 cron 解析到可查执行日志

定时任务的两个阶段

系统的定时任务会经历两个阶段:

  • 开发期:任务是自己写的,跑不跑得对由开发保证;
  • 交付后:客户要加"每天早上 8 点给未付款订单发提醒",如果没有可配置的任务模块,就只能改代码。

所以定时任务必须做成模块,而不是散落在脚本里。

任务表:把"什么时间做什么"变成数据

两条表分工明确:

  • 任务表:任务名、执行标识(handler)、cron 表达式、参数、状态、下次执行时间、最后执行结果;
  • 日志表:每次执行的开始时间、结束时间、耗时、是否成功、输出摘要、失败原因。

关键设计是把执行逻辑做成注册表:代码里注册一批可执行单元(关单、逾期扫描、静态页生成、数据备份),任务表里只存标识与参数。这样新增任务不需要改调度器,只需要注册一个新单元。

这样做还有一个额外好处:任务表可以随租户/环境不同而不同,一套代码在 A 客户那里跑 5 个任务,在 B 客户那里跑 8 个,互不影响。

cron 解析:自己写还是用库

我们选了自己实现一个最小解析器,原因有三:

  • 需要支持的标准子集很小(分、时、日、月、周五个字段,支持 *、列表、区间、步长);
  • 引入第三方库意味着多一个依赖,而私有化部署环境不允许随意 composer install;
  • 解析结果要能算出"下次执行时间",用于展示与去重,这部分逻辑必须自己掌握。

解析的实现要点是:把当前时间向下取整到分钟,逐分钟向前推进,匹配成功即返回。这种"笨办法"在分钟级调度上完全够用,且不会因为月份边界、闰年、跨年出现漏算。

执行器:三个必须解决的问题

第一,重复执行。 多进程、多实例同时扫描到同一个到期任务,会重复跑。解决方式是执行前用条件更新抢占:只有把"下次执行时间"成功推进的任务,才拿到执行权。

第二,单点失败不拖累全局。 一个任务抛异常,不能让本轮其它任务全部跳过。执行器对每个任务单独 try/catch,失败写日志、置失败状态,继续下一个。

第三,执行时长。 任务耗时过长会拖住整轮调度。做法是记录耗时,并在前端展示,超过阈值时告警,必要时把任务拆成小批次(例如静态页生成按 50 个目标一片)。

前端:让运营看得懂

任务管理页提供:列表(状态、cron、下次执行、最后结果)、启停、立即执行一次、查看执行日志、手动改 cron。

「立即执行一次」是最常用的功能:客户说"今天的日报没出来",运营点一下就能验证是任务配置问题还是数据问题。

一条经验

定时任务最容易出的不是"跑不对",而是"以为在跑其实没跑":

  • cron 写错一位,永远不触发;
  • 上次执行失败后状态被置为停用,没人发现;
  • 时区不对,任务在凌晨 4 点跑而不是 8 点。

对策只有一个:每次执行都留日志,并在管理页显式展示"下次执行时间"。让人一眼看到"它到底会不会跑"。

本文属于「开发文档」系列,记录鲜花交易系统(PHP + ThinkPHP 8 + MySQL 8)的实际研发进展。想了解功能清单与报价,可前往系统报价或联系我们。