清晨起来,客户生产环境突然出现诡异的现象,定时早上7点执行一次的任务,在七点执行了七八次,非常诡异。而且经过排查,发现没有报错信息,最后是查看日志时,发现celery
重发了很多同一时间的定时任务。
run_task.apply_async(args=[xx,xx], eta=start_time)
使用celery apply_async定时函数来实现定时周期任务,逻辑如下:
从上面可以看出,这个逻辑是可以实现定时的周期任务,而且经过验证,但定时时间不超过一个小时的时候,是可以正常周期执行任务的。但是,当定时时间超过一天时,开始出现问题,一次定时任务,出现多次执行的现象。
[2022-08-31 07:00:01,254: INFO/MainProcess] Received task: run_task[9f92844c-8cb8-458e-9917-99d51c80b0a8] eta:[2022-09-07 07:00:00]
[2022-08-31 07:00:01,297: INFO/MainProcess] Received task: run_task[f39a9992-fb5f-415e-824a-955b522d2fa7] eta:[2022-09-08 07:00:00]
[2022-08-31 07:00:01,379: INFO/MainProcess] Received task: run_task[7610c9e3-b4d9-48a1-9357-8f49d9c9fa08] eta:[2022-09-09 07:00:00]
[2022-08-31 07:00:01,423: INFO/MainProcess] Received task: run_task[0a2ff57c-eefd-4aac-9586-2d96ac761821] eta:[2022-09-10 07:00:00]
从返回的日志也可以看出,确实是在同一时间,celery产生了四个相同的worker。
出现这个现象,很可能和celery的重传机制有关。
查到一篇文章《Celery ETA任务重复提交的问题解决》,有解释这个现象。
celery对ETA/countdown/retry等要求具体时间执行的任务支持并不完整. 指定执行时间,与celery自身的失效重传机制有所冲突.
celery在没有收到任务被worker正常执行的时候就会发起重传.我项目中的ETA任务往往是在24小时之后才执行, celery的默认重传timeout是1个小时(Visibility timeout).因此理论上在ETA时间没有到之前,celery每过一个小时便重复提交一个任务给worker
修改定时周期任务逻辑,不使用apply_async实现定时周期任务,可以起一个周期执行任务,每隔两分钟执行一次,去扫描达到执行时间的定时任务,同样可以实现定时周期任务,只是任务执行可能存在0-2分钟到误差。