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

Python异步函数立即返回调度及代理缓存无阻塞实现方案

嘿,这个需求在异步服务开发里挺常见的,咱们一步步拆解实现,同时把你关心的「异步函数调度立即返回」的问题讲清楚。

核心思路

你要的效果是客户端请求时不阻塞,立即返回提示,后台异步完成API调用和缓存写入。在asyncio生态里,关键就是不要直接await你的fetch函数,而是把它包装成一个后台任务,丢给事件循环去执行,处理客户端请求的协程直接继续走返回逻辑。

具体实现步骤

我们用aiohttp作为web框架(和你用的请求库一致),同时处理缓存的并发安全问题,避免多请求冲突。

1. 基础依赖与缓存初始化

先导入需要的库,定义缓存和异步锁(异步环境下多个协程操作同一个字典会有竞态风险):

import asyncio
import aiohttp
from datetime import datetime
from aiohttp import web
# 缓存存储:key是页码,value是(响应数据, 缓存时间)
cache = {}
# 异步锁,保护缓存的读写操作
cache_lock = asyncio.Lock()

2. 优化异步请求函数

给你的fetch函数加上缓存锁,保证写入缓存时的原子性,同时处理异常情况(比如API请求失败):

async def fetch(url, page_num):
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                # 确保API返回成功,再处理数据
                response.raise_for_status()
                resp = await response.json()
                # 用锁保护缓存写入,避免并发冲突
                async with cache_lock:
                    cache[page_num] = (resp, datetime.now())
                return resp
    except Exception as e:
        # 请求失败时,清理缓存里的"正在加载"标记
        async with cache_lock:
            if page_num in cache:
                del cache[page_num]
        print(f"Failed to fetch page {page_num}: {str(e)}")

3. 处理客户端请求的核心逻辑

这里是关键:当缓存不存在时,用asyncio.create_task()把fetch丢到后台执行,立即返回提示。同时添加「正在加载」标记,避免同一页码被重复请求API:

async def proxy_handler(request):
    # 从请求参数里获取页码,默认1
    page_num = request.query.get('page', '1')
    
    async with cache_lock:
        # 检查缓存状态
        if page_num in cache:
            cache_value = cache[page_num]
            if cache_value is not None:
                # 缓存已就绪,直接返回数据
                resp_data, _ = cache_value
                return web.json_response(resp_data)
            else:
                # 正在加载中,直接返回提示
                return web.json_response({"message": "we're working on it"})
        
        # 标记为正在加载,避免重复请求
        cache[page_num] = None
    
    # 把fetch任务提交给事件循环,后台执行,不等待完成
    target_api_url = f"https://your-target-api.com/data?page={page_num}"  # 替换成实际API地址
    asyncio.create_task(fetch(target_api_url, page_num))
    
    # 立即返回提示给客户端
    return web.json_response({"message": "we're working on it"})

4. 启动代理服务器

最后把handler注册到aiohttp web应用,启动服务:

if __name__ == "__main__":
    app = web.Application()
    app.add_routes([web.get('/get-data', proxy_handler)])
    # 启动服务,监听本地8080端口
    web.run_app(app, host='0.0.0.0', port=8080)
关键知识点解释
  • asyncio.create_task():这是实现「调度异步函数立即返回」的核心。它会把你的协程包装成一个Task对象,注册到当前的事件循环中,事件循环会在空闲时自动执行这个任务。调用create_task()后,当前协程不会等待任务完成,而是直接继续执行后面的代码——这就是你要的「不阻塞客户端,立即返回」的效果。
  • 异步锁asyncio.Lock:因为多个客户端请求可能同时访问缓存,比如两个请求同时发现某页码不在缓存里,如果不加锁,会同时发起两次API请求。用锁可以保证缓存的读写操作是原子性的,避免竞态条件。
  • 「正在加载」标记:在缓存里把页码设为None,后续请求进来就知道这个数据正在加载,不会重复触发API请求,减少资源浪费。
测试验证

你可以用curl或者浏览器访问http://localhost:8080/get-data?page=1,第一次访问会立即返回{"message": "we're working on it"},等几秒后再访问同一页码,就能拿到缓存好的API数据了。

内容的提问来源于stack exchange,提问作者felisimo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 19:07:31