官方文档:Server Workers - Gunicorn with Uvicorn - FastAPI
使用 fastapi 运行命令
¶
可以直接使用fastapi run命令来启动FastAPI应用:
fastapi run main.py
如创建openapi.py文件:
python
from fastapi import FastAPI
app = FastAPI(openapi_url="/api/v1/openapi.json")
@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
然后执行:
fastapi run openapi.py
启动后显示:
INFO Using path openapi.py
INFO Resolved absolute path
/Users/skywalk/work/fastapi/openapi.py
INFO Searching for package file structure from directories with
__init__.py files
INFO Importing from /Users/skywalk/work/fastapi
╭─ Python module file ─╮
│ │
│ 🐍 openapi.py │
│ │
╰──────────────────────╯
INFO Importing module openapi
INFO Found importable FastAPI app
╭─ Importable FastAPI app ──╮
│ │
│ from openapi import app │
│ │
╰───────────────────────────╯
INFO Using import string openapi:app
╭─────────── FastAPI CLI - Production mode ───────────╮
│ │
│ Serving at: http://0.0.0.0:8000 │
│ │
│ API docs: http://0.0.0.0:8000/docs │
│ │
│ Running in production mode, for development use: │
│ │
│ fastapi dev │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Started server process [82331]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
这时候在浏览器中浏览http://192.168.0.101:8000/items/ ,会返回信息:
[{"name":"Foo"}]
这将适用于大多数情况。😎
例如,您可以使用该命令在容器、服务器等中启动FastAPI应用程序。
手动运行服务器 - Uvicorn¶
在远程服务器计算机上运行 FastAPI 应用程序所需的主要东西是 ASGI 服务器程序,例如 Uvicorn。
有 3 个主要可选方案:
- Uvicorn:高性能 ASGI 服务器。
- Hypercorn:与 HTTP/2 和 Trio 等兼容的 ASGI 服务器。
- Daphne:为 Django Channels 构建的 ASGI 服务器。
服务器主机和服务器程序¶
关于名称,有一个小细节需要记住。 💡
"服务器"一词通常用于指远程/云计算机(物理机或虚拟机)以及在该计算机上运行的程序(例如 Uvicorn)。
请记住,当您一般读到"服务器"这个名词时,它可能指的是这两者之一。
当提到远程主机时,通常将其称为服务器 ,但也称为机器 (machine)、VM (虚拟机)、节点。 这些都是指某种类型的远程计算机,通常运行 Linux,您可以在其中运行程序。
安装服务器程序¶
您可以使用以下命令安装 ASGI 兼容服务器:
- Uvicorn,一个快如闪电 ASGI 服务器,基于 uvloop 和 httptools 构建。
bash
pip install "uvicorn[standard]"
- Hypercorn,一个也与 HTTP/2 兼容的 ASGI 服务器。
bash
pip install hypercorn
Tip
通过添加
standard
,Uvicorn 将安装并使用一些推荐的额外依赖项。其中包括
uvloop
,它是asyncio
的高性能替代品,它提供了巨大的并发性能提升。
运行服务器程序¶
您可以按照之前教程中的相同方式运行应用程序,但不使用--reload
选项,例如:
bash
uvicorn main:app --host 0.0.0.0 --port 80
Hypercorn
bash
hypercorn main:app --bind 0.0.0.0:80
Warning
如果您正在使用
--reload
选项,请记住删除它。
--reload
选项消耗更多资源,并且更不稳定。它在开发 期间有很大帮助,但您不应该 在生产环境中使用它。
Hypercorn with Trio¶
Starlette 和 FastAPI 基于 AnyIO, 所以它们才能同时与 Python 的标准库 asyncio 和Trio 兼容。
尽管如此,Uvicorn 目前仅与 asyncio 兼容,并且通常使用 uvloop, 它是asyncio
的高性能替代品。
但如果你想直接使用Trio ,那么你可以使用Hypercorn,因为它支持它。 ✨
安装具有 Trio 的 Hypercorn¶
首先,您需要安装具有 Trio 支持的 Hypercorn:
bash
pip install "hypercorn[trio]"
Run with Trio¶
然后你可以传递值trio
给命令行选项--worker-class
:
bash
hypercorn main:app --worker-class trio
这将通过您的应用程序启动 Hypercorn,并使用 Trio 作为后端。
现在您可以在应用程序内部使用 Trio。 或者更好的是,您可以使用 AnyIO,使您的代码与 Trio 和 asyncio 兼容。 🎉
部署概念¶
这些示例运行服务器程序(例如 Uvicorn),启动单个进程 ,在所有 IP(0.0.0.0
)上监听预定义端口(例如80
)。
这是基本思路。 但您可能需要处理一些其他事情,例如:
- 安全性 - HTTPS
- 启动时运行
- 重新启动
- Replication(运行的进程数)
- 内存
- 开始前的步骤
在接下来的章节中,我将向您详细介绍每个概念、如何思考它们,以及一些具体示例以及处理它们的策略。 🚀
Server Workers - Gunicorn with Uvicorn¶
让我们回顾一下之前的部署概念:
- 安全性 - HTTPS
- 启动时运行
- 重新启动
- 复制(运行的进程数)
- 内存
- 启动前的先前步骤
到目前为止,通过文档中的所有教程,您可能已经在单个进程 上运行了像 Uvicorn 这样的服务器程序。
部署应用程序时,您可能希望进行一些进程复制 ,以利用多核并能够处理更多请求。
正如您在上一章有关部署概念中看到的,您可以使用多种策略。
在这里我将向您展示如何将 Gunicorn 与 Uvicorn worker 进程 一起使用。
Info
如果您正在使用容器,例如 Docker 或 Kubernetes,我将在下一章中告诉您更多相关信息:容器中的 FastAPI - Docker。
特别是,当在 Kubernetes 上运行时,您可能不想 使用 Gunicorn,而是运行 每个容器一个 Uvicorn 进程,但我将在本章后面告诉您这一点。
Gunicorn with Uvicorn Workers¶
Gunicorn 主要是一个使用WSGI标准 的应用服务器。 这意味着 Gunicorn 可以为 Flask 和 Django 等应用程序提供服务。 Gunicorn 本身与 FastAPI 不兼容,因为 FastAPI 使用最新的 ASGI 标准。
但 Gunicorn 支持充当 进程管理器 并允许用户告诉它要使用哪个特定的 worker类 。 然后 Gunicorn 将使用该类启动一个或多个 worker进程。
Uvicorn 有一个 Gunicorn 兼容的worker类。
使用这种组合,Gunicorn 将充当 进程管理器 ,监听 端口 和 IP 。 它会将通信传输 到运行Uvicorn类的worker进程。
然后与Gunicorn兼容的Uvicorn worker类将负责将Gunicorn发送的数据转换为ASGI标准以供FastAPI使用。
安装 Gunicorn 和 Uvicorn¶
bash
pip install "uvicorn[standard]" gunicorn
这将安装带有standard
扩展包(以获得高性能)的 Uvicorn 和 Gunicorn。
Run Gunicorn with Uvicorn Workers¶
接下来你可以通过以下命令运行Gunicorn:
bash
gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
输出:
bash
[19499] [INFO] Starting gunicorn 20.1.0
[19499] [INFO] Listening at: http://0.0.0.0:80 (19499)
[19499] [INFO] Using worker: uvicorn.workers.UvicornWorker
[19511] [INFO] Booting worker with pid: 19511
[19513] [INFO] Booting worker with pid: 19513
[19514] [INFO] Booting worker with pid: 19514
[19515] [INFO] Booting worker with pid: 19515
[19511] [INFO] Started server process [19511]
[19511] [INFO] Waiting for application startup.
[19511] [INFO] Application startup complete.
[19513] [INFO] Started server process [19513]
[19513] [INFO] Waiting for application startup.
[19513] [INFO] Application startup complete.
[19514] [INFO] Started server process [19514]
[19514] [INFO] Waiting for application startup.
[19514] [INFO] Application startup complete.
[19515] [INFO] Started server process [19515]
[19515] [INFO] Waiting for application startup.
[19515] [INFO] Application startup complete.
让我们看看每个选项的含义:
-
main:app
:这与 Uvicorn 使用的语法相同,main
表示名为"main
"的 Python 模块,因此是文件main.py
。app
是 FastAPI 应用程序的变量名称。-
你可以想象
main:app
相当于一个 Pythonimport
语句,例如:from main import app
-
因此,
main:app
中的冒号相当于from main import app
中的 Pythonimport
部分。
-
-
--workers
:要使用的worker进程数量,每个进程将运行一个 Uvicorn worker进程,在本例中为 4 个worker进程。 -
--worker-class
:在worker进程中使用的与 Gunicorn 兼容的工作类。-
这里我们传递了 Gunicorn 可以导入和使用的类:
import uvicorn.workers.UvicornWorker
-
-
--bind
:这告诉 Gunicorn 要监听的 IP 和端口,使用冒号 (:
) 分隔 IP 和端口。- 如果您直接运行 Uvicorn,则可以使用
--host 0.0.0.0
和--port 80
,而不是--bind 0.0.0.0:80
(Gunicorn 选项)。
- 如果您直接运行 Uvicorn,则可以使用
在输出中,您可以看到它显示了每个进程的 PID(进程 ID)(它只是一个数字)。
你可以看到:
- Gunicorn 进程管理器 以 PID
19499
开头(在您的情况下,它将是一个不同的数字)。 - 然后它开始
Listening at: http://0.0.0.0:80
。 - 然后它检测到它必须使用
uvicorn.workers.UvicornWorker
处的worker类。 - 然后它启动4个worker ,每个都有自己的PID:
19511
、19513
、19514
和19515
。
Gunicorn 还将负责管理死进程 和重新启动 新进程(如果需要保持worker数量)。 因此,这在一定程度上有助于上面列表中重启的概念。
尽管如此,您可能还希望有一些外部的东西,以确保在必要时重新启动 Gunicorn ,并且在启动时运行它等。
Uvicorn with Workers¶
Uvicorn 也有一个选项可以启动和运行多个 worker进程。
然而,到目前为止,Uvicorn 处理worker进程的能力比 Gunicorn 更有限。 因此,如果您想拥有这个级别(Python 级别)的进程管理器,那么最好尝试使用 Gunicorn 作为进程管理器。
无论如何,您都可以像这样运行它:
bash
uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
输出:
bash
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
这里唯一的新选项是 --workers
告诉 Uvicorn 启动 4 个工作进程。
您还可以看到它显示了每个进程的 PID ,父进程(这是 进程管理器 )的 PID 为27365
,每个工作进程的 PID 为:27368
、27369
, 27370
和27367
。
部署概念¶
在这里,您了解了如何使用 Gunicorn (或 Uvicorn)管理 Uvicorn 工作进程 来并行 应用程序的执行,利用 CPU 中的 多核 ,并 能够满足更多请求。
从上面的部署概念列表来看,使用worker主要有助于复制 部分,并对重新启动有一点帮助,但您仍然需要照顾其他部分:
- 安全 - HTTPS
- 启动时运行
- 重新启动
- 复制(运行的进程数)
- 内存
- 启动之前的先前步骤
容器和 Docker¶
在关于 容器中的 FastAPI - Docker 的下一章中,我将介绍一些可用于处理其他 部署概念 的策略。
我还将向您展示 官方 Docker 镜像 ,其中包括 Gunicorn 和 Uvicorn worker 以及一些对简单情况有用的默认配置。
在那里,我还将向您展示如何 从头开始构建自己的镜像 以运行单个 Uvicorn 进程(没有 Gunicorn)。 这是一个简单的过程,并且可能是您在使用像 Kubernetes 这样的分布式容器管理系统时想要做的事情。
回顾¶
您可以使用Gunicorn (或Uvicorn)作为Uvicorn工作进程的进程管理器,以利用多核CPU ,并行运行多个进程。
如果您要设置自己的部署系统,同时自己处理其他部署概念,则可以使用这些工具和想法。
请查看下一章,了解带有容器(例如 Docker 和 Kubernetes)的 FastAPI 。 您将看到这些工具也有简单的方法来解决其他部署概念。 ✨
实践
使用嗯Gunicorn启动Uvicorn workers
安装库
安装fastapi库
bash
pip install fastapi[standard]
安装uvicorn和gunicorn库
bash
pip install "uvicorn[standard]" gunicorn
启动服务
将下面代码写入myapp.py文件
bash
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
a = "a"
b = "b" + a
return {"hello world": b}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
接下来你可以通过以下命令运行Gunicorn:
bash
gunicorn myapp:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
注意,因为是绑定80端口,所以需要使用管理员权限,因为可能需要使用sudo命令:
bash
sudo gunicorn myapp:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
启动后显示:
bash
[2024-11-14 11:00:26 +0800] [7942] [INFO] Starting gunicorn 23.0.0
[2024-11-14 11:00:26 +0800] [7942] [INFO] Listening at: http://0.0.0.0:80 (7942)
[2024-11-14 11:00:26 +0800] [7942] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2024-11-14 11:00:26 +0800] [7943] [INFO] Booting worker with pid: 7943
[2024-11-14 11:00:26 +0800] [7944] [INFO] Booting worker with pid: 7944
[2024-11-14 11:00:26 +0800] [7945] [INFO] Booting worker with pid: 7945
[2024-11-14 11:00:26 +0800] [7946] [INFO] Booting worker with pid: 7946
[2024-11-14 11:00:27 +0800] [7943] [INFO] Started server process [7943]
[2024-11-14 11:00:27 +0800] [7943] [INFO] Waiting for application startup.
[2024-11-14 11:00:27 +0800] [7943] [INFO] Application startup complete.
[2024-11-14 11:00:27 +0800] [7945] [INFO] Started server process [7945]
[2024-11-14 11:00:27 +0800] [7945] [INFO] Waiting for application startup.
[2024-11-14 11:00:27 +0800] [7945] [INFO] Application startup complete.
[2024-11-14 11:00:27 +0800] [7944] [INFO] Started server process [7944]
[2024-11-14 11:00:27 +0800] [7944] [INFO] Waiting for application startup.
[2024-11-14 11:00:27 +0800] [7944] [INFO] Application startup complete.
[2024-11-14 11:00:27 +0800] [7946] [INFO] Started server process [7946]
[2024-11-14 11:00:27 +0800] [7946] [INFO] Waiting for application startup.
[2024-11-14 11:00:27 +0800] [7946] [INFO] Application startup complete.
[2024-11-14 11:01:22 +0800] [7942] [INFO] Handling signal: winch
现在一套FastAPI的Web服务就启动了。在生产环境中,我们一般外面会再放一层nginx。
nginx配置
使用Gunicorn启动5000和5001端口的Uvicorn workers
bash
gunicorn myapp:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:5000
gunicorn myapp:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:5001
写example.com域名nginx配置
假设域名是example.com, 那么在nginx的原来默认配置文件里,加上一句include example.com.conf:
bash
default_type application/octet-stream;
include example.com.conf;
然后写文件example.com.conf文件:
bash
upstream example.com{
ip_hash;
server 127.0.0.1:5000;
server 127.0.0.1:5001;
}
server {
listen 80;
# optional ssl configuration
listen 443 ssl;
ssl_certificate /path/to/ssl/pem_file;
ssl_certificate_key /path/to/ssl/certificate_key;
# end of optional ssl configuration
server_name example.com;
access_log /var/log/nginx/access.log;
location / {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Host $host:$server_port;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 60s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
proxy_buffering off;
proxy_temp_file_write_size 64k;
proxy_pass http://example.com;
proxy_redirect off;
}
}
这样一台真正的WEB服务器就启动完成了。在升级扩充服务器的时候,只需要再换更好的服务器, gunicorn启动更多的进程即可。如果扩充了更多服务器,则可以启动5002、5003等更多的端口,nginx这里只要再加上更多的流量均衡服务器配置即可。
bash
upstream example.com{
ip_hash;
server 127.0.0.1:5000;
server 127.0.0.1:5001;
server 192.168.1.101:5002;
server 10.0.0.12:5003;
}