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失效如何解决?

11 回复

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依赖系统时钟,而分布式环境下各节点时间不同步会导致计时混乱。

解决方案:

  1. 强制时间同步(最根本) 在所有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
    
  2. 代码层面增加容错处理

    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"
    
  3. 使用相对时间替代绝对时间

    [@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"
    
  4. 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

受教,下次注意。

回到顶部