在Uvicorn/FastAPI中发起下游HTTP请求的正确方式是什么?
FastAPI并发场景下正确使用requests库的方案
错误原因解析
你遇到的h11._util.LocalProtocolError: can't handle event type ConnectionClosed when role=SERVER and state=SEND_RESPONSE错误,本质是requests.Session的线程不安全特性导致的。Session的连接复用机制在多线程共享实例时,会出现连接状态混乱——比如一个线程正在发送响应,另一个线程却关闭了共享连接,触发协议状态冲突。
三种正确使用方式
1. 单请求独立创建实例(最简单)
如果对性能要求不高,直接在接口里每次调用requests.get/post,每个请求用独立的连接,完全规避线程安全问题。
示例代码:
from fastapi import FastAPI import requests app = FastAPI() @app.get("/fetch-external-data") def fetch_data(): # 每次请求都新建请求实例 response = requests.get("https://目标外部API地址") response.raise_for_status() return response.json()
缺点:没有连接复用,高并发下会频繁建立/销毁TCP连接,性能略差。
2. 线程局部存储隔离Session(兼顾复用与安全)
利用threading.local为每个线程分配独立的Session实例,既保留连接复用的性能优势,又避免线程间的状态干扰。
示例代码:
from fastapi import FastAPI import requests import threading app = FastAPI() # 线程局部存储,每个线程拥有专属Session thread_local = threading.local() def get_thread_session(): if not hasattr(thread_local, "session"): thread_local.session = requests.Session() return thread_local.session @app.get("/fetch-external-data") def fetch_data(): session = get_thread_session() response = session.get("https://目标外部API地址") response.raise_for_status() return response.json()
优点:平衡了连接复用和线程安全,适合需要复用连接的高并发场景。
3. 切换异步HTTP客户端(最优解)
FastAPI本身支持异步,改用httpx.AsyncClient这类异步客户端,完全契合框架的异步模型,天然支持高并发,不存在线程安全问题,性能也更优。
示例代码(单次请求创建客户端):
from fastapi import FastAPI import httpx app = FastAPI() @app.get("/fetch-external-data") async def fetch_data(): async with httpx.AsyncClient() as client: response = await client.get("https://目标外部API地址") response.raise_for_status() return response.json()
如果要复用连接池,可全局管理客户端实例:
from fastapi import FastAPI import httpx app = FastAPI() async_client = None @app.on_event("startup") async def init_client(): global async_client async_client = httpx.AsyncClient() @app.on_event("shutdown") async def close_client(): await async_client.aclose() @app.get("/fetch-external-data") async def fetch_data(): response = await async_client.get("https://目标外部API地址") response.raise_for_status() return response.json()
这是FastAPI并发场景下最推荐的方案,异步架构+连接池复用能最大化性能。
总结
- 低并发场景:直接用requests单请求实例,简单省心。
- 需要连接复用的高并发场景:用线程局部存储隔离Session。
- 追求最优性能:切换到httpx异步客户端,适配FastAPI异步模型。
内容的提问来源于stack exchange,提问作者SteveJ
相关产品推荐
相关产品推荐

