You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 22:45:30