Files

6.4 KiB
Raw Permalink Blame History

id, title, type, domain, stack, tags, level, related, week
id title type domain stack tags level related week
python.fastapi.interview FastAPI 面试题 interview python
fastapi
FastAPI
pydantic
依赖注入
面试
intermediate
python.fastapi.routing-validation
python.fastapi.deps-auth-errors
python.fastapi.streaming-testing
4
5
16

FastAPI 面试题

基础

【基础】FastAPI 有什么特点?为什么 AI 服务常用它?

参考答案:基于 Starlette(ASGI)和 pydantic,原生支持 async,适合 IO 密集的模型调用;通过类型标注自动完成参数解析和校验、生成 OpenAPI 文档;依赖注入方便组织鉴权、数据库会话和测试替换;支持流式响应和 WebSocket。AI 生态(模型 SDK、LangChain、向量库客户端)以 Python 为主,用 FastAPI 封装服务很自然。

追问:FastAPI 和 Flask、Django 怎么选?→ AI 服务、异步 I/O、需要自动接口文档时选 FastAPI;需要完整后台管理和 ORM 的传统业务系统可以选 Django。

【基础】FastAPI 怎么区分路径参数、查询参数和请求体?

参考答案:在路径模板中出现的参数是路径参数;简单类型(int、str、bool 等)的其他参数是查询参数;pydantic 模型类型的参数是请求体;请求头、Cookie 用 Header()、Cookie() 显式声明。也可以用 Path()、Query()、Body() 显式指定并添加校验规则。

追问:请求体可以是多个 Pydantic 模型吗?→ 可以,多个模型参数会按参数名嵌套在 JSON 中;单个模型参数想嵌套时用 Body(embed=True)。

【基础】路由函数用 async def 和 def 有什么区别?

参考答案:async def 在事件循环中直接执行,里面只能调用非阻塞的异步代码;普通 def 会被放到线程池中执行,适合调用同步阻塞库。在 async def 中调用阻塞函数会卡住整个事件循环,是最常见的性能问题。

追问:def 路由的线程池大小有限制吗?→ 有,默认由 AnyIO 的线程池限制(默认 40 个线程,以官方文档为准),大量阻塞调用会排队。

【基础】响应模型有什么作用?

参考答案:声明接口返回的数据结构:自动过滤掉未声明的字段(防止泄露内部字段),校验和序列化返回数据,并体现在 OpenAPI 文档中,前端可以据此生成类型。

追问:怎么避免把密码等字段返回给前端?→ 定义单独的输出模型(不包含敏感字段)并设置为 response_model,而不是直接返回数据库对象。

进阶

【进阶】Depends 的原理和常见用途?

参考答案:FastAPI 在处理请求时解析路由函数签名,递归解析依赖树,按顺序调用依赖函数并把返回值注入参数;同一请求内相同依赖默认只调用一次;带 yield 的依赖可以在请求结束后执行清理。用途:鉴权和获取当前用户 / 租户、数据库会话、分页参数、共享客户端;测试时通过 dependency_overrides 替换。

追问:依赖中的 yield 有什么用?→ yield 之前创建资源(数据库会话),之后清理资源;测试时可以用 app.dependency_overrides 替换依赖。

【进阶】中间件和依赖都能做鉴权,怎么选?

参考答案:中间件作用于所有请求,适合与业务无关的横切逻辑:请求 ID、日志、耗时、CORS。依赖按路由或路由器声明,能拿到类型化的结果(当前用户)并注入处理函数,能在测试中替换,适合鉴权、权限、租户。通常鉴权用依赖。

追问:中间件能访问路由的参数吗?→ 中间件在路由匹配之前执行,拿不到解析后的参数;需要按路由做权限控制的,用依赖。

【进阶】怎么实现大模型的流式输出?要注意什么?

参考答案:用 StreamingResponse 包装异步生成器,media_type="text/event-stream",每个事件 data: ...\n\n。注意:响应头发出后不能改状态码,错误要以事件形式发送;检查客户端断开及时停止生成;内容用 JSON 编码避免换行破坏格式;经过 Nginx 时关闭缓冲;前端 POST 请求需要用 fetch 读取流。

追问:客户端中途断开,服务端还在调用大模型怎么办?→ 在生成器中检查 await request.is_disconnected(),或者依赖取消机制,及时停止上游调用以节省成本。

场景题

【场景题】压测发现 FastAPI 服务 QPS 很低,CPU 也不高,可能是什么原因?

参考答案:最常见的是在 async def 路由里调用了阻塞操作(同步数据库驱动、requests、time.sleep、同步 SDK),事件循环被阻塞。其他原因:每个请求新建 HTTP 客户端或数据库连接、连接池太小、下游(模型、数据库)本身慢、只启动了一个 worker。排查:看慢请求的耗时分布,用 py-spy 看事件循环线程在做什么。

追问:怎么验证是不是事件循环被阻塞了?→ 开启 asyncio 的调试模式(会记录执行过慢的回调),或者在压测时观察一个简单的 /health 接口延迟是否也升高。

【场景题】一个接口依赖大模型和向量数据库,怎么写单元测试?

参考答案:把模型客户端和检索器抽象成接口(Protocol),通过依赖注入获取;测试中用 app.dependency_overrides 替换为假实现,返回固定内容或模拟异常;用 TestClient 调用接口,断言状态码、响应格式、流式事件顺序、错误处理。真实模型的效果评估另外做(评测集),不放在单元测试里。

追问:测试流式接口要注意什么?→ 用 TestClient 的 stream 方法或 httpx 异步客户端逐块读取,断言事件格式和结束标记,而不只是最终文本。

【场景题】给多个业务系统提供 AI 能力,怎么防止某个系统把额度用光?

参考答案:每个调用方一个 API Key,鉴权依赖解析出租户;在服务端按租户做限流(Redis 滑动窗口 / 令牌桶)和配额(按日 / 月的 token 数);记录每次调用的 token 用量写入数据库;超过配额返回 429 并给出提示;对异常增长告警。Nginx 层可以再加一层按 IP 的粗粒度限流。

追问:额度用完时返回什么?→ 返回 429 和明确的错误码,带上 Retry-After 头;同时通知对应业务方,提供额度查询接口。