ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI多Agent协作系统实战(十):同一份报告刷屏3小时——Cron定时任务的“幽灵重复“之谜

AI多Agent协作系统实战(十):同一份报告刷屏3小时——Cron定时任务的“幽灵重复“之谜 系列第10篇 | 为什么心跳报告每隔5分钟就重复发送同一份内容追踪一个隐藏在时间格式中的Bug前言今天凌晨发现飞书群里每隔5分钟就收到一条相同的心跳报告持续了3个多小时。内容一模一样任务状态一模一样就像一个幽灵在重复发送同一条消息。这到底是怎么发生的为什么定时任务会重复执行为什么节流throttle机制没有生效追踪下去发现了一个隐藏在时间格式中的致命Bug。一、事件经过1.1 发现报告重复用户发消息“怎么一直在重复的报这个啥问题”报告内容【小密统筹报告 06-30 07:02】 整体进度0/1 0% | # | 任务ID | 完善内容 | 开发 | 测试 | |---|--------|----------|------|------| | 1 | FIX-SERVICE-CREATE-BOTTOM-GAP-COLOR-0629-V3 | 工单创建底部间距颜色V3修复 | ⏳ | - |每隔5分钟就发一次从07:02开始一直持续到10点多。1.2 检查Cron Job检查心跳cronjobcronjob list发现cronjob正常运行每5分钟触发一次。但问题是为什么每次触发都输出相同的内容1.3 检查节流机制心跳脚本有节流逻辑# 检查节流文件throttle_file/tmp/hermes-heartbeat-throttleifos.path.exists(throttle_file):withopen(throttle_file,r)asf:last_runfloat(f.read().strip())iftime.time()-last_run300:# 5分钟内不重复returnNone理论上5分钟内不应该重复输出。但实际却重复了。1.4 发现时间格式Bug检查节流文件cat/tmp/hermes-heartbeat-throttle输出20260630_001831这是一个字符串不是时间戳问题根源节流文件存储的是字符串格式的时间20260630_001831而不是Unix时间戳1782774517。last_runfloat(20260630_001831)# ValueError!float()无法解析这个字符串抛出异常。异常被try-except捕获节流检查被跳过每次都执行。1.5 修复Bug修复节流逻辑# 修复前throttle_file/tmp/hermes-heartbeat-throttleifos.path.exists(throttle_file):withopen(throttle_file,r)asf:last_runfloat(f.read().strip())iftime.time()-last_run300:returnNone# 修复后throttle_file/tmp/hermes-heartbeat-throttleifos.path.exists(throttle_file):try:withopen(throttle_file,r)asf:contentf.read().strip()# 兼容两种格式时间戳和字符串ifcontent.isdigit():last_runfloat(content)else:# 字符串格式转换为时间戳fromdatetimeimportdatetime last_rundatetime.strptime(content,%Y%m%d_%H%M%S).timestamp()iftime.time()-last_run300:returnNoneexceptException:pass# 解析失败跳过节流同时修复写入节流文件的代码确保写入Unix时间戳# 修复前withopen(throttle_file,w)asf:f.write(datetime.now().strftime(%Y%m%d_%H%M%S))# 修复后withopen(throttle_file,w)asf:f.write(str(time.time()))二、技术分析2.1 节流机制的原理Cron触发 → 检查节流文件 → ├── 文件存在且5分钟 → 跳过 └── 文件不存在或5分钟 → 执行 → 写入节流文件2.2 Bug的传播链时间格式字符串 → float()解析失败 → 异常被捕获 → 节流检查被跳过 → 每次都执行 → 报告重复2.3 为什么持续3小时Cron每5分钟触发一次每次触发都输出报告用户没有及时发现直到用户手动检查才发现三、经验总结3.1 时间格式要统一内部计算用Unix时间戳time.time()存储和显示用ISO格式datetime.now().isoformat()不要混用两种格式3.2 异常处理要谨慎# ❌ 错误异常被静默吞掉try:last_runfloat(content)except:pass# 节流失效但没有日志# ✅ 正确异常要记录日志try:last_runfloat(content)exceptExceptionase:logger.warning(f节流文件解析失败:{e})# 可以选择跳过节流但要记录3.3 节流机制要健壮支持多种时间格式解析失败时有默认行为记录日志便于排查3.4 监控重复输出检测短时间内相同内容的重复输出超过阈值时报警自动暂停重复的cronjob四、涉及文件文件修改内容xiaomi_heartbeat_check.py修复节流逻辑兼容多种时间格式throttle_file确保存储Unix时间戳
返回列表