• uwsgi部署flask,flask_apscheduler任务遇到各种问题解决


    背景:最近在做的全域事件项目,快要靠近尾声了,需要用到uwsgi部署至生产环境,由于之前是debug模式,运行项目也是通过命令 python manager.py runserver (manage是通过flask_script创建的脚本管理,用于类似django的数据库初始化、迁移和管理app等操作)。现项目由uwsgi管理,由于uwsgi一些特性造成项目运行中出现的一系列问题,顺便记录下解决方案。

    uwsgi.ini

     1 [uwsgi]
     2 # 项目文件夹
     3 chdir = xxx/xxx/xxx
     4 # wsgi文件路经
     5 wsgi-file = xxx/xxx/xxx/manage.py
     6 # 回调的app对象
     7 callback = manager
     8 # 虚拟环境路经
     9 home = /home/xxx/.virtualenvs/xxx
    10 # 主进程
    11 master = true
    12 # 最大输了的工作进程
    13 processes = 2
    14 # 项目中使用的IP:端口
    15 http = xxx.xxx.xxx.xxx:5000
    16 # 退出的时候是否清理环境
    17 vacuum = true
    18 # uwsgi日志文件路经
    19 daemonize = /home/xxx/xxx/xxx/uwsgi.log
    20 # 进程pid文件
    21 pidfile = /home/xxx/xxx/xxx/uwsgi.pid

    一、uwsgi通过callback回调app,manager虽管理app,但并不能提供uwsgi所用到的回调的app,直接如上配置callback=manager,接口请求在uwsgi.log中会看到__callback__错误,因为数据库迁移后manager没什么用,因此将manager换成原app,如下:

    manager.py

    1 from xxx import create_app
    2 
    3 # 创建flask应用对象
    4 app = create_app("product")
    5 
    6 
    7 if __name__ == '__main__':
    8 
    9     app.run()

    uwsgi.ini修改callback

    1 # 回调的app对象
    2 callback = app

    二、flask_apscheduler定时任务不启动

    uwsgi启动后在没有请求的时候,部分进程会被挂起,需在uwsgi.ini中增加如下配置:

    1 # flask_apscheduler配置
    2 enable-threads = true
    3 preload = true
    4 lazy-apps = true

    三、uwsgi启动后,定时任务启动两次,重复启动

    原因uwsgi中开启了两个进程,进程独享一份资源,因此两个进程都启动了各自的scheduler,网上找了许久解决方案后,最终确定用文件锁的方式解决

    create_app函数中

     1 from xxx.tasks import scheduler
     2 import os
     3 import atexitcc
     4 import fcntl
     5 
     6 def create_app(config_name):
     7     app = Flask(__name__, template_folder="static/")
     8     。。。
     9 
    10     # 使用app初始化任务
    11     # 使用文件锁,解决flask_apscheduler定时任务重复启动问题
    12     f = open(os.path.join(BASE_DIR, "xxx/xxx/scheduler.lock"), "wb")
    13     try:
    14         fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
    15         scheduler.init_app(app)
    16         scheduler.start()
    17     except:
    18         pass
    19 
    20     def unlock():
    21         fcntl.flock(f, fcntl.LOCK_UN)
    22         f.close()
    23 
    24     # 注册退出事件,如果flask项目退出,则解除scheduler.lock文件锁并关闭文件
    25     atexit.register(unlock)

    四、uwsgi启动项目后,config中配置的定时任务正常运行且不会重复启动,uwsgi.log中也可以看到scheduler正常启动;但通过api启动,使用scheduler.add_job()方式启动的任务,从uwsgi.log中发现是等待scheduler start状态,由此发现还是创建了两个scheduler对象。看了许久代码后发现了问题

    因为项目中需要运行的任务较多,任务函数也都是需要区分开写在不同py文件中的,因此为了方便管理,我在项目中创建了tasks文件夹(python包),在tasks中的__init__.py文件中定义了scheduler对象,tasks中的各任务文件引用此scheduler并使用

    tasks.__init__.py

     1 from flask_apscheduler import APScheduler
     2 from apscheduler.schedulers.background import BackgroundScheduler
     3 from multiprocessing import Queue
     4 
     5 # APScheduler任务对象
     6 scheduler = APScheduler(BackgroundScheduler(timezone="Asia/Shanghai"))
     7 
     8 # 任务流
     9 event_info_q = Queue()
    10 
    11 # 处理的任务中间件列表
    12 middleware_li = []

    因此在uwsgi开启多进程情况下,scheduler还是因多进程创建了多次,加上文件锁的原因,仅一个scheduler被启动,于是改动如下:

    tasks.__init__.py

    1 # APScheduler任务对象
    2 scheduler = None
    3 
    4 # 任务流
    5 event_info_q = Queue()
    6 
    7 # 处理的任务中间件列表
    8 middleware_li = []

    create_app函数

     1 from flask_apscheduler import APScheduler
     2 from apscheduler.schedulers.background import BaskgroundScheduler
     3 from xxx import tasks
     4 import os
     5 import atexit
     6 import fcntl
     7 
     8 def create_app(config_name):
     9     app = Flask(__name__, template_folder="static/")
    10     。。。
    11 
    12     # 使用app初始化任务
    13     # 使用文件锁,解决flask_apscheduler定时任务重复启动问题
    14     f = open(os.path.join(BASE_DIR, "xxx/xxx/scheduler.lock"), "wb")
    15     try:
    16         fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
    17         tasks.scheduler = APScheduler(BaskgroundScheduler(timezone="Asia/Shanghai"))
    18         tasks.scheduler.init_app(app)
    19         tasks.scheduler.start()
    20     except:
    21         pass
    22 
    23     def unlock():
    24         fcntl.flock(f, fcntl.LOCK_UN)
    25         f.close()
    26 
    27     # 注册退出事件,如果flask项目退出,则解除scheduler.lock文件锁并关闭文件
    28     atexit.register(unlock)

    因为项目启动后首先调用的是create_app,create_app中将tasks.__init__.py中的scheduler重置为了APScheduler对象,并初始化app和启动,因此tasks文件夹内的各任务文件引用scheduler对象不会出现问题。

  • 相关阅读:
    产品开发协作流程
    iOS性能优化
    iOS地图国内和国外的偏差
    Apple技术支持
    iOS应用架构谈 网络层设计方案
    form表单重复被提交
    SpringBoot+filter+listener+拦截器
    SpringBoot中的异常处理
    谷歌浏览器突然不能使用,并且出现报错码"STATUS_INVALID_IMAGE_HASH"解决方法
    form提交(图片,excel其他文件)
  • 原文地址:https://www.cnblogs.com/zzmx0/p/15980028.html
Copyright © 2020-2023  润新知