Сервер Uvicorn неожиданно отключился

Я работаю с фреймворком FastAPI, обслуживаемым сервером Uvicorn. Мое приложение должно выполнять требующие много времени численные вычисления в заданной конечной точке (/run). Для этого я использую «background_task» от fastAPI (который в основном является «background_task» от Starlette).

При запуске приложения после некоторого времени номинального поведения сервер по какой-то причине отключается.

Логи из приложения выглядят так:

INFO: Started server process [922]
INFO: Waiting for application startup.
DEBUG: None - ASGI [1] Started
DEBUG: None - ASGI [1] Sent {'type': 'lifespan.startup'}
DEBUG: None - ASGI [1] Received {'type': 'lifespan.startup.complete'}
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
DEBUG: ('10.0.2.111', 57396) - Connected
DEBUG: ('10.0.2.111', 57397) - Connected
DEBUG: ('10.0.2.111', 57396) - ASGI [2] Started
DEBUG: ('10.0.2.111', 57396) - ASGI [2] Received {'type': 'http.response.start', 'status': 200, 'headers': '<...>'}
INFO: ('10.0.2.111', 57396) - "GET /run HTTP/1.1" 200
DEBUG: ('10.0.2.111', 57396) - ASGI [2] Received {'type': 'http.response.body', 'body': '<32 bytes>'}
DEBUG: ('10.0.2.111', 57396) - ASGI [3] Started
DEBUG: ('10.0.2.111', 57396) - ASGI [3] Received {'type': 'http.response.start', 'status': 404, 'headers': '<...>'}
INFO: ('10.0.2.111', 57396) - "GET /favicon.ico HTTP/1.1" 404
DEBUG: ('10.0.2.111', 57396) - ASGI [3] Received {'type': 'http.response.body', 'body': '<22 bytes>'}
DEBUG: ('10.0.2.111', 57396) - ASGI [3] Completed

...

DEBUG: ('10.0.2.111', 57396) - Disconnected
... The background task is completed.
DEBUG: ('10.0.2.111', 57396) - ASGI [2] Completed
DEBUG: ('10.0.2.111', 57397) - Disconnected
DEBUG: ('10.0.2.111', 57405) - Connected

...
The application goes on, with requests and completed background tasks.
At some point, during the execution of a background task:

INFO: Shutting down
DEBUG: ('10.0.2.111', 57568) - Disconnected
DEBUG: ('10.0.2.111', 57567) - Disconnected
INFO: Waiting for background tasks to complete. (CTRL+C to force quit)
DEBUG: ('10.0.2.111', 57567) - ASGI [6] Completed
INFO: Waiting for application shutdown.
DEBUG: None - ASGI [1] Sent {'type': 'lifespan.shutdown'}
DEBUG: None - ASGI [1] Received {'type': 'lifespan.shutdown.complete'}
DEBUG: None - ASGI [1] Completed
INFO: Finished server process [922]

Я действительно не понимаю, почему это происходит. Я понятия не имею, что попробовать, чтобы исправить это.

Мой код выглядит так.

#!/usr/bin/env python3.7
import time
from fastapi import FastAPI, BackgroundTasks
import uvicorn
from starlette.responses import JSONResponse
import my_imports_from_project

analysis_api = FastAPI()

@analysis_api.get("/")
def root():
    return {"message": "root"}


@analysis_api.get("/test")
def test():
    return {"message": "test"}

@analysis_api.get("/run")
def run(name: str, background_task: BackgroundTasks):
    try:
        some_checks(name)
    except RaisedExceptions:
        body = {"running": False,
                "name": name,
                "cause": "Not found in database"}
        return JSONResponse(status_code=400, content=body)
    body = {"running": True,
            "name": name}
    background_task.add_task(run_analysis, name)
    return JSONResponse(status_code=200, content=body)


if __name__ == "__main__":
    uvicorn.run("api:analysis_api", host="0.0.0.0", log_level="debug")


person Davide Melli    schedule 15.07.2019    source источник
comment
Вы пробовали запускать uvicorn через gunicorn? gunicorn может продолжить процесс   -  person Will    schedule 15.07.2019
comment
Мне интересно, вызывает ли завершение фоновых задач запрос на выключение каким-либо образом   -  person Will    schedule 15.07.2019
comment
О gunicorn: * Бывает то же самое, но как минимум gunicord перезапускает сервер. О задаче, вызывающей отключение: * Не знаю, она просто производит вычисления и сохраняет результаты в базе данных. Более того, некоторые задачи выполняются успешно и только одна закрывает сервер (все задачи равны)   -  person Davide Melli    schedule 15.07.2019


Ответы (1)


Вот так я решил всю проблему.

Я думаю, что проблема была в том, что мои задачи порождали какие-то процессы для выполнения вычислений. Итак, вместо использования FastApi background_task теперь я использую multiprocessing.Process(). Это решает это.

Как отметили ребята из FastApi, это решение может плохо масштабироваться, если проект станет большим и сложным. В этом случае настоятельно рекомендуется использовать что-то вроде очереди сообщений + запущенной задачи (как предлагается на Сайт FastApi.

Однако для небольших проектов решение с multiprocessing.Process или subprocess.Popen вполне подходит.

person Davide Melli    schedule 17.07.2019