接外包,有相关需求的可以联系我:Telegram | Email

如何修复Django使用gunicorn部署apscheduler产生重复任务的问题?

该文章创建(更新)于12/6/2022,请注意文章的时效性!

之前一直用的uwsgi进行部署的服务,后来部署好几套程序后发现安装特别依赖于C环境,好几次安装把服务器干崩了。后来有次GitHub发生崩溃发现用的gunicron部署的,自己也就尝试了。用着确实挺爽的,但最近发现apscheduler的任务总是多次重复执行,研究了一下是gunicron部署多进程导致的锅,看了一圈最终解决如下所示:

解决

gunicorn启动脚本添加--preload

我是没采用的,具体也没研究

gunicorn xxxx.wsgi:application --bind 0.0.0.0:8000 --workers 2 --timeout 30 --daemon --access-logfile access.log --error-logfile error.log --capture-output --enable-stdio-inheritance --log-level=critical --preload
  • 看了一圈说是副作用如下:

虽然这样可以使用scheduler创建代码只执行一次,但是问题也在于它只执行一次,重新部署以后如果用kill -HUP重启gunicorn,它并不会重启,甚至整个项目都不会更新。这是preload的副作用,除非重写部署脚本,完全重启应用。

gunicorn和gevent结合

这个是通过chatGPT获取到的答案,我只能说牛逼!

  • 安装
pip install gevent
  • 启动脚本

我这尝试发现没日志输出就暂时放弃了

gunicorn -k gevent finance.wsgi:application --bind 0.0.0.0:8000 --workers 2 --timeout 30 --daemon --access-logfile access.log --error-logfile error.log --capture-output --enable-stdio-inheritance --log-level=critical

文件锁

目前用的这个,这里注意的一点是scheduler选用类型是BackgroundScheduler,用BlockingScheduler这种阻塞模式发现任务不会运行也没有任何报错,具体原因有待考察。

import atexit
import fcntl
from apscheduler.schedulers.background import BlockingScheduler, BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore, register_job

scheduler = BackgroundScheduler()
# scheduler = BlockingScheduler() # Not Working
scheduler.add_jobstore(DjangoJobStore(), "default")

@register_job(scheduler, "interval", hours=24, id='xxx', max_instances=1,
              replace_existing=True)  # 用于配置每天生成文章
def del_old_image():
    pass

f = open("scheduler.lock", "wb")
try:
    fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
    scheduler.start()
except Exception as scheduler_exception:
    logger.error(scheduler_exception)

def unlock():
    fcntl.flock(f, fcntl.LOCK_UN)
    f.close()

atexit.register(unlock)

闲杂: chatGPT回答使用gevnet的例子

我是被这个答案震惊了🤯

在 Django 中使用 gunicorn 部署 apscheduler 时,可能会出现重复任务的问题。这是因为 gunicorn 默认会在每个请求之后 fork 一个新的进程,这会导致 apscheduler 在每个进程中都会创建一个新的任务。

要解决这个问题,需要配置 gunicorn 使用 gevent 模式,这样它就不会 fork 新的进程了。要使用 gevent 模式,你需要在 gunicorn 启动命令中添加 -k gevent 选项,例如:

gunicorn -k gevent myproject.wsgi

另外,你还需要确保 apscheduler 的任务只在主进程中运行,而不是在每个 fork 出来的子进程中。你可以通过在 apscheduler 中配置 add_job 方法的 misfire_grace_time 参数来实现这一点。

例如,你可以这样配置 apscheduler:

from apscheduler.schedulers.blocking import BlockingScheduler

sched = BlockingScheduler()

@sched.scheduled_job('interval', minutes=1, misfire_grace_time=60)
def my_job():
    # Do something

sched.start()

这样,当 gunicorn 使用 gevent 模式运行时,apscheduler 就只会在主进程中运行,避免了任务重复执行的问题。

希望这些信息能帮到你! © ChatGPT

参考


要不赞赏一下?

微信
支付宝
PayPal
Bitcoin

版权声明 | Copyright

除非特别说明,本博客所有作品均采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。转载请注明转自-
https://www.emperinter.info/2022/12/06/how-to-fix-the-issue-of-django-using-gunicorn-to-deploy-apscheduler-that-generates-duplicate-tasks/


要不聊聊?

我相信你准备留下的内容是经过思考的!【勾选防爬虫,未勾选无法留言】

*

*



YouTube | B站

微信公众号

👉 NewsLetter ❤️ 邮箱订阅 👈

优惠码

阿里云国际版20美元
Vultr10美元
搬瓦工 | Bandwagon应该有折扣吧?
Just My SocksJMS9272283 【注意手动复制去跳转】
域名 | namesiloemperinter(1美元)
币安 币安