fastapi 异步处理请求机制
"""
第五步:异步版框架
目标:理解 async/await —— 协程是"可暂停的函数",await 是暂停点,
暂停时把控制权交还事件循环,让别的任务先跑。
关键改动:
- handle / solve 变成 async 函数。
- 依赖函数可以是 async def:解析时若发现是协程函数,就 await 它。
- 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 秒)

浙公网安备 33010602011771号