之前一直用的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