当我启用 uwsgi 线程支持并启动调度程序时,api 停止工作

When I enable uwsgi thread support and start the scheduler the api stops working

我是 python、flask、nginx 和所有这些东西的新手。

我有一个 flask 应用程序作为前端的 API。此外,当 flask 应用程序启动时,我想使用 APScheduler 启动计划任务。

问题是,当我启用 uwsgi 线程支持并启动调度程序时,api 停止工作(504 网关超时)。但调度程序的工作方式与日志文件中所示相同。当我删除调度程序/线程支持时,api 可以工作,但我显然不再有调度程序了。 我怀疑调度程序以某种方式阻止了烧瓶应用程序 运行 正确?

由于我对这些技术不熟悉,因此我将 post 下面的设置。如果您需要更多文件信息,请告诉我。(整个过程是 raspberry pi 上的 运行,api 是通过局域网从我的电脑访问的)

app.service

[Unit]
Description=uWSGI instance to serve app
After=network.target

[Service]
User=pi
Group=www-data
WorkingDirectory=/home/pi/flask
Environment="PATH=/home/pi/flask/appenv/bin"
ExecStart=/home/pi/flask/appenv/bin/uwsgi --ini app.ini --enable-threads

[Install]
WantedBy=multi-user.target

app.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

socket = /home/pi/flask/app.sock
chmod-socket = 660
vacuum = true

die-on-term = true

app.py

#!/usr/bin/env python3

from flask import Flask, request
from apscheduler.schedulers.background import BackgroundScheduler

import logging
logging.basicConfig(filename='logfile.log',level=logging.DEBUG)

from api.Controller import Controller
from Handler.Handler import Handler
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor

api_controller = Controller()
handler = Handler()

def startHandlerJob():
    handler.ExecuteAllSensors()

app = Flask( __name__ )


@app.route('/app')
def apiDefinition():
    return 'API Definition: GetHumidityValues, TODO'

@app.route( "/app/GetHumidityValues", methods=["GET"] )
def GetHumidityValues():
    logging.info("app.py: API-call GetHumidityValues")
    return api_controller.GetHumidityValues()


if (__name__ == "__main__"):
    app.run(host='0.0.0.0')

executors = {
    'default': ThreadPoolExecutor(20),
    'processpool': ProcessPoolExecutor(5)
}
job_defaults = {
    'coalesce': False,
    'max_instances': 1
}

scheduler = BackgroundScheduler(daemon=True, executors=executors, job_defaults=job_defaults)
scheduler.start()
scheduler.add_job(startHandlerJob,'cron', minute='*')

logfile.log

WARNING:apscheduler.scheduler:已跳过作业"startHandlerJob (trigger: cron[minute=''], next run at: 2019-12-18 19:01:00 CET)" 的执行:已达到 运行ning 实例的最大数量 (1) DEBUG:apscheduler.scheduler:下次唤醒时间为 2019-12-18 19:02:00+01:00(59.980780 秒后) DEBUG:apscheduler.scheduler:正在寻找 运行 的工作 WARNING:apscheduler.scheduler:已跳过作业 "startHandlerJob (trigger: cron[minute=''], next run at: 2019-12-18 19:02:00 CET)" 的执行:已达到 运行ning 实例的最大数量 (1) DEBUG:apscheduler.scheduler:下一次唤醒时间为 2019-12-18 19:03:00+01:00(59.979407 秒后)

systemctl 状态应用程序

* app.service - uWSGI instance to serve app
   Loaded: loaded (/etc/systemd/system/app.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2019-12-18 18:40:57 CET; 23min ago
 Main PID: 21129 (uwsgi)
    Tasks: 8 (limit: 2200)
   Memory: 22.9M
   CGroup: /system.slice/app.service
           |-21129 /home/pi/flask/appenv/bin/uwsgi --ini app.ini --enable-threads
           |-21148 /home/pi/flask/appenv/bin/uwsgi --ini app.ini --enable-threads
           |-21149 /home/pi/flask/appenv/bin/uwsgi --ini app.ini --enable-threads
           |-21150 /home/pi/flask/appenv/bin/uwsgi --ini app.ini --enable-threads
           |-21151 /home/pi/flask/appenv/bin/uwsgi --ini app.ini --enable-threads
           `-21152 /home/pi/flask/appenv/bin/uwsgi --ini app.ini --enable-threads

Dec 18 18:40:57 raspberrypi uwsgi[21129]: mapped 386400 bytes (377 KB) for 5 cores
Dec 18 18:40:57 raspberrypi uwsgi[21129]: *** Operational MODE: preforking ***
Dec 18 18:40:59 raspberrypi uwsgi[21129]: WSGI app 0 (mountpoint='') ready in 2 seconds on interpreter 0xa6f900 pid: 21129 (default app)
Dec 18 18:40:59 raspberrypi uwsgi[21129]: *** uWSGI is running in multiple interpreter mode ***
Dec 18 18:40:59 raspberrypi uwsgi[21129]: spawned uWSGI master process (pid: 21129)
Dec 18 18:40:59 raspberrypi uwsgi[21129]: spawned uWSGI worker 1 (pid: 21148, cores: 1)
Dec 18 18:40:59 raspberrypi uwsgi[21129]: spawned uWSGI worker 2 (pid: 21149, cores: 1)
Dec 18 18:40:59 raspberrypi uwsgi[21129]: spawned uWSGI worker 3 (pid: 21150, cores: 1)
Dec 18 18:40:59 raspberrypi uwsgi[21129]: spawned uWSGI worker 4 (pid: 21151, cores: 1)
Dec 18 18:40:59 raspberrypi uwsgi[21129]: spawned uWSGI worker 5 (pid: 21152, cores: 1)

日志文件显示调度程序处于活动状态。但是当我尝试使用 http:/raspberryipaddress/app 时,答案是 504 网关超时响应。当我删除调度程序并禁用线程支持时,此调用将按预期工作。

感谢任何帮助。可能我遗漏了一些明显的东西,因为我对所有这些东西都是陌生的。 谢谢!

我有点犹豫要不要把这个作为答案,但这里是...

在uwsgi app.ini设置进程后(你的情况是5,我用4)我也设置了 threads = 2。我不知道这是否与 --enable-threads 选项有任何直接关系,因为这似乎是为了让您的应用程序启动它自己的线程,但它可能有助于 uwsgi 在每个进程中拥有自己的线程。 UWSGI 文档还指出(某处)进程越多不一定越好。

此外,您的日志文件显示来自调度程序的警告,表明已达到 运行ning 实例的最大数量。我认为这意味着您给调度程序的工作没有在下一个预定 运行(1 分钟后)之前完成?如果是这样,它是否卡在某个地方,阻止了其他一切?

最后,如果一切都失败了,文档中的其他内容 (UWSGI Security and availability)

A common problem with webapp deployment is “stuck requests”. All of your threads/workers are stuck (blocked on request) and your app cannot accept more requests. To avoid that problem you can set a harakiri timer.It is a monitor (managed by the master process) that will destroy processes stuck for more than the specified number of seconds (choose harakiri value carefully).

最后,最后 :) 有一个类似 top 的 uwsgi 监控工具,可以方便地查看发生了什么,只是

pip install uwsgitop
uwsgitop 127.0.0.1:9191

加上 telnet 到 adress:port 显然可以向您显示更多信息(我自己还没有尝试过)。

好的,谢谢你的回答。我没有让它与您建议的解决方案一起使用。因此,我简单地决定 运行 调度程序作为单独的 systemd 服务。这样调度就不会阻止烧瓶 api 工作。

我遇到了完全相同的问题。我的解决方法是使用 ProcessPoolExecutor 而不是 ThreadPoolExecutor.

由于某些未知原因(至少我不知道),当 ThreadPoolExecutor 中有多个线程时,该死的问题就出现了。

在我将大多数 cpu-bound 线程更改为进程后(即对这些作业使用 ProcessPoolExecutor),该死的问题消失了...