FastAPI简单使用
FastAPI简单使用
1 介绍
FastAPI是一个高性能的Web框架,支持自动数据验证**、**自动文档生成和异步。
https://fastapi.tiangolo.com/
Uvicorn是一款基于ASGI协议的高性能Python Web服务器,专为异步框架设计,支持FastAPI、Starlette等现代Web框架。
1.1 异步编程
在Python中异步编程中,关键字async 和 await很重要:
async 的作用
- 用于定义一个协程函数(coroutine function)。
- 调用
async
函数时,不会立即执行函数体,而是返回一个 协程对象(coroutine object),需要被事件循环调度执行。
await的作用
- 用于暂停当前协程,等待一个 awaitable 对象(如另一个协程、
asyncio.sleep()
、asyncio.Future
等)完成。 - 不会阻塞事件循环,而是让出控制权,允许其他协程运行。
await
只能在async def
函数中使用。
示例
import asyncio# 异步调用数据
async def fetch_data():print("Hello")await asyncio.sleep(5)print("World")# 调用不会执行,而是返回协程对象
coro = fetch_data()
print(type(coro))async def main():# 等待 fetch_data() 完成result = await fetch_data()print("获取到的结果:", result)asyncio.run(main())
1.2 异步和多线程的区别
在 Python 中,异步(asyncio) 和 多线程(threading) 都可以实现“同时干多件事”,但它们的底层机制、性能表现和适用场景完全不同。下面用一句话总结:
异步是“单线程内协作式并发”,多线程是“操作系统级抢占式并发”。
IO 高并发用 async,简单并发用 thread,CPU 密集用 process
维度 | 异步(asyncio) | 多线程(threading) |
---|---|---|
核心机制 | 事件循环 + 协程(用户态调度) | 操作系统线程(内核态调度) |
线程数 | 单线程(默认) | 多线程 |
切换开销 | 极低(用户态切换) | 较高(内核态上下文切换) |
GIL 影响 | 不受影响(单线程) | 受 GIL 限制(CPU 密集任务无法并行) |
适合任务 | 高并发 I/O 密集(如爬虫、API 请求) | 少量 I/O 密集或简单并发任务 |
编程难度 | 较高(需写 async/await ) | 较低(同步代码即可) |
数据竞争 | 无(单线程) | 有(需加锁、同步) |
1.3 异步≠立即响应客户端
假设你有这样一个异步路径操作函数:
from fastapi import FastAPI
import asyncioapp = FastAPI()@app.get("/")
async def read_root():# 模拟一个耗时的 I/O 操作,比如数据库查询await asyncio.sleep(2)return {"message": "Hello World"}
当一个请求 GET /
到来时,会发生以下步骤:
- 接收请求:FastAPI 从客户端接收到 HTTP 请求。
- 创建任务:FastAPI 创建一个任务来执行你的
read_root
函数。 - 事件循环接管:FastAPI 将你的函数(一个协程)交给 ASGI 服务器(如 Uvicorn 或 Hypercorn)内部的事件循环。
- 执行直到第一个
await
:事件循环开始执行read_root
函数。 - 遇到
await
(关键点!):- 当执行到
await asyncio.sleep(2)
时,函数会说:“我要睡眠 2 秒,在这期间我什么都不做,你去忙别的吧!” - 此时,事件循环会立即挂起这个函数。
- 事件循环不会阻塞等待,它会立刻去处理其他正在排队的任务(可能是另一个来自客户端的请求,或者是其他异步操作)。
- 当执行到
- 等待完成:2 秒后,睡眠操作完成。
- 恢复执行:事件循环在适当的时机(当它轮询到睡眠完成的事件时)重新唤醒
read_root
函数,并从await
之后的地方继续执行。 - 返回响应:函数执行到最后,返回
{"message": "Hello World"}
。 - 发送响应:FastAPI 将这个 Python 字典转换为 JSON,并通过 HTTP 响应发送回客户端。
1.4 同步 vs 异步
为了更好地理解,我们看一个同步的例子:
import time@app.get("/sync")
def read_root_sync():# 模拟一个耗时的 I/O 操作time.sleep(2) # 注意:这里是同步的 sleepreturn {"message": "Hello World Sync"}
对于这个同步端点:
- 当执行到
time.sleep(2)
时,整个工作线程被彻底阻塞 2 秒。 - 在这 2 秒内,这个线程不能做任何其他事情,即使有新的请求进来,它也无法处理。
- 如果并发请求数超过了工作线程数,新请求就必须排队等待,导致性能急剧下降。
1.5 FastAPI的特点
- FastAPI 的异步机制并不能让你的业务逻辑执行得更快。一个需要 2 秒的数据库查询,在异步函数里仍然需要 2 秒才能拿到结果。
- 它的巨大优势在于提升服务器的并发吞吐量。通过在等待 I/O 时释放线程,让有限的服务器资源可以同时服务更多的客户端连接。
- 你“立即”得到的是事件循环的解放,而不是给客户端的最终响应。
因此,你应该将异步用于那些主要是 I/O 密集型 的操作(如网络请求、数据库查询、文件读写),而不是 CPU 密集型 的操作(如复杂的数学计算)。对于 CPU 密集型任务,你应该使用 def
定义普通函数,并让 FastAPI 在单独的线程池中运行它们。
2 简单使用
2.1 安装依赖环境
pip install "fastapi[standard]"
pip install uvicorn
2.2 简单使用FastAPI
import asyncio
from typing import Unionimport uvicorn
from fastapi import FastAPI, BackgroundTasks, Query
from pydantic import BaseModel, Fieldapp = FastAPI()"""
FastAPI中参数校验的关键字有Field、Query、Path。
其中Field和Query的作用相似,不同点是字段 Field,参数 Query/Path,写在模型里 → Field;写在函数签名里 → Query/Path。
三个点 (...) 被称为 Ellipsis(省略号),它是一个内置常量,主要作为占位符和切片工具使用。
"""class Item(BaseModel):name: strprice: floatis_offer: Union[bool, None] = Noneclass TaskInfo(BaseModel):task_id: int = Field(..., ge=1, le=120, description="任务编号")@app.get("/read")
def read_item(item_id: int = Query(..., ge=1, le=120, description="数据编号"), data: Union[str, None] = None):return {"item_id": item_id, "data": data}@app.post("/update/{item_id}")
async def update_item(item_id: int, item: Item):# time.sleep(10)await asyncio.sleep(4)return {"item_id": item_id, "item_name": item.name}async def long_running_task(task_id: int):# 模拟耗时操作await asyncio.sleep(10)print(f"任务 {task_id} 完成")@app.post("/task")
async def create_task(task_info: TaskInfo, background_tasks: BackgroundTasks):# 将任务添加到后台执行background_tasks.add_task(long_running_task, task_info.task_id)return {"status": "任务已接收","task_id": task_info.task_id,"message": "任务正在后台处理"}if __name__ == '__main__':uvicorn.run(app, host="127.0.0.1", port=3000)
2.3 返回值
(1)接口文档地址
http://127.0.0.1:3000/docs
(2)读取数据地址
http://127.0.0.1:3000/read
(3)更新数据,不会立刻返回值
http://127.0.0.1:3000/update/1
参数
{"name": "张三","price": 0.12,"is_offer": true
}
(4)异步任务,立刻返回值。
http://127.0.0.1:3000/task