fastapi 异步处理请求机制

"""
第五步:异步版框架

目标:理解 async/await —— 协程是"可暂停的函数",await 是暂停点,
暂停时把控制权交还事件循环,让别的任务先跑。

关键改动:

  1. handle / solve 变成 async 函数。
  2. 依赖函数可以是 async def:解析时若发现是协程函数,就 await 它。
  3. asyncio.gather 让多个请求并发处理 —— IO 等待时间会重叠。

对应 fastapi:你可以写 async def 路由和 async def 依赖,
一个请求在 await(等数据库)时,服务器能同时处理别的请求。
"""

import asyncio
import inspect


class Depend:
    def __init__(self, dependency):
        self.dependency = dependency


class App:
    def __init__(self):
        self.routes = {}

    def get(self, path):
        def decorator(func):
            self.routes[("GET", path)] = func
            return func
        return decorator

    async def solve(self, func, raw_params, cache=None):
        if cache is None:
            cache = {}
        kwargs = {}
        for name, param in inspect.signature(func).parameters.items():
            if isinstance(param.default, Depend):
                dep_func = param.default.dependency
                if dep_func in cache:
                    kwargs[name] = cache[dep_func]
                else:
                    dep_kwargs = await self.solve(dep_func, raw_params, cache)  # 递归,注意 await
                    result = dep_func(**dep_kwargs)
                    # 依赖本身可能是 async def:那样 result 是个协程,需要 await 出真正的值
                    if inspect.iscoroutine(result):
                        result = await result
                    cache[dep_func] = kwargs[name] = result
            else:
                if name in raw_params:
                    type_ = param.annotation
                    kwargs[name] = type_(raw_params[name]) if type_ is not inspect.Parameter.empty else raw_params[name]
                elif param.default is not inspect.Parameter.empty:
                    kwargs[name] = param.default
                else:
                    raise ValueError(f"缺少参数: {name}")
        return kwargs

    async def handle(self, method, path, raw_params):
        func = self.routes.get((method, path))
        if func is None:
            return "404 Not Found"
        kwargs = await self.solve(func, raw_params)
        result = func(**kwargs)
        if inspect.iscoroutine(result):   # 路由函数本身也可能是 async def
            result = await result
        return result


app = App()


# 一个"慢"的异步依赖:模拟查数据库要等 1 秒
async def get_user_from_db(uid: int):
    print(f"    [db] 查用户 {uid} 中...(await 1 秒)")
    await asyncio.sleep(1)
    print(f"    [db] 用户 {uid} 查到了")
    return f"用户{uid}"


@app.get("/profile")
async def profile(user=Depend(get_user_from_db)):
    return f"档案: {user}"


async def main():
    import time
    t0 = time.time()
    # 并发处理两个请求。各自都要在依赖里 await 1 秒,但会重叠。
    results = await asyncio.gather(
        app.handle("GET", "/profile", {"uid": "1"}),
        app.handle("GET", "/profile", {"uid": "2"}),
    )
    print("结果:", results)
    print(f"两个请求总耗时 {time.time()-t0:.2f} 秒(串行会是 2 秒)")


if __name__ == "__main__":
    asyncio.run(main())

输出结果:

    [db] 查用户 1 中...(await 1 秒)
    [db] 查用户 2 中...(await 1 秒)
    [db] 用户 1 查到了
    [db] 用户 2 查到了
结果: ['档案: 用户1', '档案: 用户2']
两个请求总耗时 1.00 秒(串行会是 2 秒)
posted @ 2026-07-03 12:47  RolandHe  阅读(3)  评论(0)    收藏  举报