Python分布式任务队列Celery中时间不同步导致soft_time_limit失效如何解决?
RT,客户端和服务端应该已经实行了 NTP 同步:
# service ntp restart
# ntpdate pool.ntp.org
9 Jan 08:28:19 ntpdate[8247]: the NTP socket is in use, exiting
Server 和 client 端的相关配置:
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = False
#这里标识下,CELERY_ENABLE_UTC True 和 False 都试过了,没用
本地 worker 运行时的时间:
celery -A test worker -E -l INFO -n hostA --concurrency=2
[2019-01-09 08:29:04,904: INFO/MainProcess]
server 端运行 flower 时,命令行显示的时间:
celery -A test flower --port=5555
[W 190109 08:29:04 state:122] Substantial drift from celery@test may mean clocks are out of sync. Current drift is 57600 seconds. [orig: 2019-01-09 08:29:04.870091 recv: 2019-01-10 00:29:04.931890]
然鹅,在 flower 的 web 界面上,显示我某个卡住的任务是在 57600s 后的未来接受的任务,然后就一直 starting 卡在那儿了。 后来 Ctrl+c 才开始执行,报了个 broker 超时重连,然后把这个卡住的任务跑完了,才停止。 其实这里也没搞明白为啥。
我猜测是因为这个任务开始时间超过了我任务,我的 soft_time_limit 也没有起作用。
求解,如何同步 server 端收到的时间,希望不再有时差,找了很久没有解决。
很急,在线等大佬!
Python分布式任务队列Celery中时间不同步导致soft_time_limit失效如何解决?
celery 的时区 BUG 好像一直都存在,还没有修复
https://github.com/celery/django-celery-beat/issues/80#issuecomment-373615356
celery.py<br>app = Celery('test')<br>app.now = datetime.now # 关键在这里<br>
settings.py<br>CELERY_TIMEZONE = 'Asia/Shanghai'<br>DJANGO_CELERY_BEAT_TZ_AWARE = False<br>CELERY_ENABLE_UTC = False<br>TIME_ZONE = 'Asia/Shanghai'<br>USE_TZ = False<br>
这个问题我遇到过,核心原因是Celery的soft_time_limit依赖系统时钟,而分布式环境下各节点时间不同步会导致计时混乱。
解决方案:
-
强制时间同步(最根本) 在所有Celery worker和broker节点配置NTP服务:
# Ubuntu/Debian sudo apt install ntpdate sudo ntpdate pool.ntp.org # 或使用chrony(推荐) sudo apt install chrony sudo systemctl enable --now chronyd -
代码层面增加容错处理
from celery.exceptions import SoftTimeLimitExceeded import time [@app](/user/app).task(soft_time_limit=30) def my_task(): start_time = time.time() try: # 你的任务逻辑 while time.time() - start_time < 25: # 提前5秒自检 # 处理逻辑 time.sleep(1) # 如果接近超时,主动清理退出 if time.time() - start_time > 28: cleanup_resources() return "timeout_handled" except SoftTimeLimitExceeded: cleanup_resources() return "soft_timeout" -
使用相对时间替代绝对时间
[@app](/user/app).task(bind=True, soft_time_limit=30) def my_task(self): deadline = time.time() + 25 # 设置内部截止时间 while time.time() < deadline: # 正常处理 pass # 主动退出避免触发soft_time_limit return "completed_before_limit" -
Docker环境特别处理 在Dockerfile中确保时区同步:
RUN apt-get update && apt-get install -y tzdata ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime
总结:强制NTP同步是根本,代码中增加主动超时检查作为补充。
刚才试了,好像还是不管用…
我把 celery 升级到 3.1.7 解决了,小版本升级太烦人。
设置个环境变量 export TZ = = 'Asia/Shanghai’
为啥不直接升到 4
抱歉,乌龙了,好像不是版本的原因。
我原来的是 3.1.25 ,改成了 3.1.7,先是成功了一下,后来发现 worker 好像一直 offline。
连着切了几个版本都这样,我后来又换回 3.1.25 ,莫名其妙暂时没有问题了,暂时还没跑偏。
我本来环境变量就是 shanghai,这个我确定。
我晚点再测测,还没找出啥原因。
我曾在这里测过:
>>> from celery.utils.timeutils import utcoffset
>>> utcoffset()
worker 和 client,一个 8,一个 8,刚好是相差 57600s。
现在莫名其妙两个都变成-8 了。
忘了回复您另一个问题,我这边需要用到 3.x 版本以上废弃的一个功能,也就是使用 test.delay()调用 chord 聚合执行,所以不能升级太高。
稍微读下时间获取部分源码就知道了 还能有莫名其妙的
还有, 时间同步把 ntp 服务开了不要跑 ntpdate
Grep 了 ntpdate,进程好像后台没有跑 ntpdate,就执行了一次。
唉,紧急处理,没想到去读源码,实在心急看不下去。
ntp 服务和 ntpdate 是不同的
系统有有相关代码来调整时间的 ntpdate 是属于强制改时间的
有可能触发时间回退 导致定时器重复触发
不要用 ntpdate 来调时间!!!
python 时间本来就比较简单,怎么封装也不会复杂 随便读一下就知道 Celery 怎么获取时间的了
又不是要你去读 system call
受教,下次注意。

