FastAPI + Uvicorn + 多线程。如何让 Web 应用程序并行处理多个请求?

问题描述 投票:0回答:1

我是 Python 开发的新手。 (但我有doenet背景) 我有一个简单的 FastAPI 应用程序

from fastapi import FastAPI
import time
import logging
import asyncio
import random

app = FastAPI()
r = random.randint(1, 100)
logging.basicConfig(level="INFO", format='%(levelname)s | %(asctime)s | %(name)s | %(message)s')
logging.info(f"Starting app {r}")

@app.get("/")
async def long_operation():
    logging.info(f"Starting long operation {r}")
    await asyncio.sleep(1)
    time.sleep(4) # I know this is blocking and the endpoint marked as async, but I actually do have some blocking requests in my code.
    return r

然后我使用这个命令运行应用程序:

uvicorn "main:app" --workers 4

应用程序在不同进程中启动 4 个实例:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started parent process [22112]
INFO | 2023-05-11 12:32:43,544 | root | Starting app 17
INFO:     Started server process [10180]  
INFO:     Waiting for application startup.
INFO:     Application startup complete.   
INFO | 2023-05-11 12:32:43,579 | root | Starting app 58
INFO:     Started server process [29592]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO | 2023-05-11 12:32:43,587 | root | Starting app 12
INFO:     Started server process [7296]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO | 2023-05-11 12:32:43,605 | root | Starting app 29
INFO:     Started server process [15208]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

然后我打开 3 个浏览器选项卡并开始尽可能并行地向应用程序发送请求。这是日志:

INFO | 2023-05-11 12:32:50,770 | root | Starting long operation 29
INFO:     127.0.0.1:55031 - "GET / HTTP/1.1" 200 OK
INFO | 2023-05-11 12:32:55,774 | root | Starting long operation 29
INFO:     127.0.0.1:55031 - "GET / HTTP/1.1" 200 OK
INFO | 2023-05-11 12:33:00,772 | root | Starting long operation 29
INFO:     127.0.0.1:55031 - "GET / HTTP/1.1" 200 OK
INFO | 2023-05-11 12:33:05,770 | root | Starting long operation 29
INFO:     127.0.0.1:55031 - "GET / HTTP/1.1" 200 OK
INFO | 2023-05-11 12:33:10,790 | root | Starting long operation 29
INFO:     127.0.0.1:55031 - "GET / HTTP/1.1" 200 OK
INFO | 2023-05-11 12:33:15,779 | root | Starting long operation 29
INFO:     127.0.0.1:55031 - "GET / HTTP/1.1" 200 OK
INFO | 2023-05-11 12:33:20,799 | root | Starting long operation 29
INFO:     127.0.0.1:55031 - "GET / HTTP/1.1" 200 OK
INFO | 2023-05-11 12:33:25,814 | root | Starting long operation 29
INFO:     127.0.0.1:55031 - "GET / HTTP/1.1" 200 OK
INFO | 2023-05-11 12:33:30,856 | root | Starting long operation 29
INFO:     127.0.0.1:55031 - "GET / HTTP/1.1" 200 OK

我的观察:

  1. 只有 1 个进程在工作。别人不处理requests(我试过很多次了,总是这样)
  2. 创建了 4 个不同的实例。

我的问题:

  1. 为什么只有一个过程有效而其他过程无效?
  2. 如果我想有内存缓存。我能做到吗?
  3. 我可以运行 1 个可以并行处理一定数量请求的进程吗?
  4. 这是否与我在 Windows 上进行测试有关?
python multithreading fastapi uvicorn
1个回答
1
投票
  1. 为什么只有一个过程有效而其他过程无效?

我无法重现你的观察。事实上,我不知道你是如何推断出来的。如果我更改您的日志记录格式并添加

logging.basicConfig(level="INFO", format='%(process)d | %(levelname)s | %(asctime)s | %(name)s | %(message)s')

(注意打印进程 ID 的

%(process)d
)然后我在日志中看到

19968 | INFO | 2023-05-11 12:45:53,297 | root | Starting long operation 35
21368 | INFO | 2023-05-11 12:45:56,112 | root | Starting long operation 90
5268 | INFO | 2023-05-11 12:45:56,626 | root | Starting long operation 3
22024 | INFO | 2023-05-11 12:45:57,032 | root | Starting long operation 19
5268 | INFO | 2023-05-11 12:45:57,416 | root | Starting long operation 3
22024 | INFO | 2023-05-11 12:45:57,992 | root | Starting long operation 19

在并行产生多个请求之后。您是否有可能错误地触发了您的请求?不是并行的吗?

无论如何,所有的工人都被利用了。然而,选择它们的确切方式是一个实现细节。

  1. 如果我想有内存缓存。我能做到吗?

你的意思是工人之间共享?并不真地。您可以进行一些跨进程通信(例如共享内存),但这并不容易做到和维护。通常我们会为每个进程使用一个内存缓存。除非你受到记忆的限制,否则它确实成为一个问题。

  1. 我可以运行 1 个可以并行处理一定数量请求的进程吗?

我不确定我是否理解你的问题。如果你愿意,你可以用

--workers 1
运行 uvicorn,没问题。 Python 的默认异步运行时是单线程的,因此您不会获得真正的并行性。而是并发,类似于 JavaScript 的工作方式。因此你需要小心,你必须避免像
time.sleep
这样的阻塞调用,而使用像
asyncio.sleep
这样的非阻塞调用。好吧,对于异步编程,无论您生成多少个进程,您在这样做时都必须小心。

  1. 这是否与我在 Windows 上进行测试有关?

不,这与操作系统无关。这种设计是由于 Python 本身的主要缺陷:它具有 GIL(全局解释器锁),这使得与 dotnet/C# 等其他运行时相比,线程的用处大大降低。在 Python 中,真正的并行性是通过子进程实现的。

© www.soinside.com 2019 - 2024. All rights reserved.