FastAPI分页异常:聚合REST资源时重复调用外部API
问题描述
我在FastAPI应用中实现了/job_search端点,用于从多个RESTful资源获取职位数据,经归一化后返回给调用方。但分页功能未达预期:请求第二页结果时,所有外部REST API会被再次调用,响应耗时与第一页相近。我根据fastapi-pagination文档以为分页会将所有结果存入内存,后续请求可直接使用,请问我哪里理解错了?
代码示例
@router.get("/job_search", response_model=Page[Job], summary="Get jobs from various sites based on keyword", tags=["Job search"]) @has_permissions(Permissions.PUBLIC_PERMISSION_LEVEL) def search_jobs(keyword: str = Query(), user: User = Depends(user_db_crud.get_current_user)): """ Returns jobs found in various sites based on keyword. """ try: jobs = search_jobs(preprocess_string(keyword)) return paginate(jobs) except Exception as e: raise HTTPException( status_code=500, detail=f"An unexpected error occurred. Report this message to support: {e}") @timing_decorator def search_jobs(): url = "some url here" response = requests.get(url) . . process here . . return jobs
日志信息
INFO: Started server process [19894] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:9999 (Press CTRL+C to quit) Function search_jobs took 48.73508310317993 seconds to run. INFO: 127.0.0.1:55747 - "GET /v1/job_search?keyword=%CE%BF%CE%B4%CE%B7%CE%B3%CE%BF%CF%82&page=1&size=100 HTTP/1.1" 200 OK Function search_jobs took 52.42994689941406 seconds to run. INFO: 127.0.0.1:55752 - "GET /v1/job_search?keyword=%CE%BF%CE%B4%CE%B7%CE%B3%CE%BF%CF%82&page=2&size=100 HTTP/1.1" 200 OK
分析与解决方案
你对fastapi-pagination的核心逻辑理解有误:paginate()函数本身不会自动缓存全量结果,它只是对传入的数据集做内存内的分页切片操作,没有跨请求的持久化能力。
你的代码逻辑问题在于:
- 每次请求(不管是第1页还是第N页),都会重新调用
search_jobs(),触发外部API请求和全量数据处理,之后才对返回的jobs做分页。这就是为什么每次请求耗时接近的原因——全量数据的获取流程完全重复了。
要解决这个问题,核心是缓存同关键词的全量查询结果,让后续分页请求直接复用缓存数据,无需重复调用外部API。具体实现方式如下:
1. 引入缓存机制
可以用内存缓存(比如functools.lru_cache)或分布式缓存(比如Redis),以下是基于lru_cache的示例:
from functools import lru_cache # 确保preprocess_string处理后的keyword是可哈希类型(比如字符串) @lru_cache(maxsize=128) # 根据业务场景调整缓存容量 @timing_decorator def search_jobs(keyword: str): url = "some url here" response = requests.get(url) # ... 数据处理逻辑 ... return jobs
2. 关键注意事项
- 如果外部职位数据需要定期更新,需给缓存设置过期时间。
lru_cache可结合定时清理逻辑,或改用支持TTL(生存时间)的缓存方案(如Redis)。 - 必须确保
preprocess_string(keyword)返回的是可哈希类型,否则lru_cache无法生效。 - 如果查询参数不止
keyword,需将所有影响查询结果的参数都加入缓存键中。
3. 原逻辑失效的本质
fastapi-pagination的paginate()是无状态的:它仅处理当前请求传入的数据集,不会在请求之间保存任何数据。每次请求到达时,端点函数都会从头执行,重新获取全量数据后再做分页——分页操作只减少了返回给客户端的数据量,并没有减少全量数据的获取开销。
内容的提问来源于stack exchange,提问作者Chris Karvouniaris
相关产品推荐
相关产品推荐

