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 asyncio

app = FastAPI()

@app.get("/")
async def read_root():
    # 模拟一个耗时的 I/O 操作,比如数据库查询
    await asyncio.sleep(2)
    return {"message": "Hello World"}

当一个请求 GET / 到来时,会发生以下步骤:

  1. 接收请求:FastAPI 从客户端接收到 HTTP 请求。
  2. 创建任务:FastAPI 创建一个任务来执行你的 read_root 函数。
  3. 事件循环接管:FastAPI 将你的函数(一个协程)交给 ASGI 服务器(如 Uvicorn 或 Hypercorn)内部的事件循环。
  4. 执行直到第一个 await:事件循环开始执行 read_root 函数。
  5. 遇到 await(关键点!)
    • 当执行到 await asyncio.sleep(2) 时,函数会说:“我要睡眠 2 秒,在这期间我什么都不做,你去忙别的吧!”
    • 此时,事件循环会立即挂起这个函数
    • 事件循环不会阻塞等待,它会立刻去处理其他正在排队的任务(可能是另一个来自客户端的请求,或者是其他异步操作)。
  6. 等待完成:2 秒后,睡眠操作完成。
  7. 恢复执行:事件循环在适当的时机(当它轮询到睡眠完成的事件时)重新唤醒 read_root 函数,并从 await 之后的地方继续执行。
  8. 返回响应:函数执行到最后,返回 {"message": "Hello World"}
  9. 发送响应:FastAPI 将这个 Python 字典转换为 JSON,并通过 HTTP 响应发送回客户端。

1.4 同步 vs 异步

为了更好地理解,我们看一个同步的例子:

import time

@app.get("/sync")
def read_root_sync():
    # 模拟一个耗时的 I/O 操作
    time.sleep(2)  # 注意:这里是同步的 sleep
    return {"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 Union

import uvicorn
from fastapi import FastAPI, BackgroundTasks, Query
from pydantic import BaseModel, Field

app = FastAPI()

"""
FastAPI中参数校验的关键字有Field、Query、Path。
其中Field和Query的作用相似,不同点是字段 Field,参数 Query/Path,写在模型里 → Field;写在函数签名里 → Query/Path。
三个点 (...) 被称为 Ellipsis(省略号),它是一个内置常量,主要作为占位符和切片工具使用。
"""


class Item(BaseModel):
    name: str
    price: float
    is_offer: Union[bool, None] = None


class 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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值