部署在Azure App Service的FastAPI Docker应用抛出TaskCanceledException问题求助
问题背景
我将一个基于FastAPI + SQLAlchemy(PostgreSQL Supabase)的应用通过Docker容器部署在Azure App Service上,已配置服务保持活跃,不存在冷启动问题。但应用会不定期在日志中出现错误,同时发生挂起——API完全无响应,既不超时也不返回结果,目前未发现明确的触发规律。网上未找到相关文档或类似案例,想问问有没有人遇到过该问题,以及对应的解决方法是什么?
错误日志
Failed to forward request to http://1xx.xx.xx.2:8000. Encountered a System.Threading.Tasks.TaskCanceledException exception after 300021.816ms with message: The request was canceled due to the configured HttpClient.Timeout of 300 seconds elapsing.. Check application logs to verify the application is properly handling HTTP traffic.
已尝试的优化措施
- 提升数据库配置规格
- 使用Supabase提供的连接池服务
- 缓存数据库查询结果以减少数据库调用
- 提升Azure App Service的配置规格
相关代码片段
# db.py SQLALCHEMY_DATABASE_URL = os.environ["SQLALCHEMY_DATABASE_URL"] def get_engine(): return create_engine(SQLALCHEMY_DATABASE_URL, pool_pre_ping=True) SessionLocal = sessionmaker( autocommit=False, autoflush=False, bind=get_engine()) # main.py def get_db(): db = SessionLocal() try: yield db db.commit() except: # 发生任何异常时回滚事务 db.rollback() raise finally: db.close() # 路由中通过Depends传入会话 def test_auth0(token=Depends(token_auth_scheme), db: Session = Depends(get_db)): # 路由逻辑省略
可能的解决思路与方案
1. 排查应用内部请求阻塞问题
Azure的超时提示核心是:转发层把请求发给容器的8000端口后,容器未在300秒内返回响应,导致转发超时。这说明问题大概率在应用内部,而非单纯资源不足(你已升级过规格)。
- 给数据库查询加强制超时:在
create_engine中添加连接参数,防止慢查询卡住整个请求,比如:
如果有长时间运行的业务逻辑,必须改成异步执行(比如用Celery+Redis),不能让请求线程一直挂起等待。create_engine( SQLALCHEMY_DATABASE_URL, pool_pre_ping=True, connect_args={"options": "-c statement_timeout=30000"} # 30秒超时 ) - 调整事务生命周期:你当前在
yield db之后才执行commit,意味着整个请求处理过程中事务始终处于打开状态。如果请求中包含耗时操作,会长期占用数据库连接,甚至耗尽连接池导致后续请求排队挂起。建议在业务逻辑完成后手动触发commit,或者优化事务范围,避免不必要的长事务。另外,异常捕获不要用宽泛的except:,改为捕获具体的数据库异常,防止吞掉其他类型的错误。
2. 检查Docker容器网络与进程状态
- 确认FastAPI监听地址:容器内的FastAPI必须监听
0.0.0.0:8000,不能只监听127.0.0.1,否则Azure转发层无法访问。 - 排查僵死进程:通过Azure日志或
docker exec进入容器,查看是否有挂起的Python进程。如果存在,大概率是某个请求触发了死锁或无限循环,需要结合请求日志定位具体代码。
3. 调整Azure App Service转发配置
- 关闭ARR粘性会话:如果开启了
ARR Affinity,可能导致请求集中在某个实例上引发过载,建议关闭该选项测试。 - 确认端口映射:检查
WEBSITES_PORT配置是否和容器内FastAPI的监听端口一致,避免端口不匹配导致转发失败。
4. 增加应用级日志监控
给每个请求添加唯一ID,记录请求进入、数据库操作开始/结束、响应返回的时间点,定位卡环节点。比如添加FastAPI中间件:
from fastapi import Request import uuid import time @app.middleware("http") async def log_request_details(request: Request, call_next): request_id = str(uuid.uuid4()) print(f"[{request_id}] 请求开始: {request.method} {request.url}") start_time = time.time() response = await call_next(request) process_duration = time.time() - start_time print(f"[{request_id}] 请求结束: 耗时{process_duration:.2f}s,状态码{response.status_code}") return response
5. 优化SQLAlchemy连接池配置
结合Supabase的连接池服务,调整本地连接池参数避免冲突:
create_engine( SQLALCHEMY_DATABASE_URL, pool_pre_ping=True, pool_size=10, # 根据并发量调整 max_overflow=20, pool_recycle=300 # 小于Supabase PGBouncer的超时时间,防止连接被回收后复用 )
内容的提问来源于stack exchange,提问作者Savio

