如何在Flask中返回202 Accepted且不终止函数?
Flask中实现先返回202 Accepted再处理异步搜索任务的方案
首先明确:HTTP是请求-响应一对一的模型,你没法在同一个请求里先返回202状态码,之后再返回结果。必须通过「异步任务+结果查询」的方式实现需求,以下是两种可行方案:
一、简单场景:用Python线程实现(适合开发/小流量场景)
用threading模块把耗时搜索放到后台线程,先返回202并给用户一个任务ID,让用户后续通过ID轮询结果。
from flask import Flask, jsonify, request import threading import time import uuid app = Flask(__name__) # 临时存储任务结果,生产环境建议替换为Redis或数据库 task_results = {} def long_running_search(task_id, query): # 模拟长时间搜索逻辑 time.sleep(10) # 实际场景替换为真实搜索逻辑 result = f"搜索关键词「{query}」找到100条匹配结果" task_results[task_id] = {"status": "completed", "result": result} @app.route("/search") def search(): query = request.args.get("q", "") if not query: return jsonify({"error": "请提供搜索关键词"}), 400 # 生成唯一任务ID task_id = str(uuid.uuid4()) # 初始化任务状态 task_results[task_id] = {"status": "processing"} # 启动后台线程执行搜索 thread = threading.Thread(target=long_running_search, args=(task_id, query)) thread.daemon = True # 线程随应用退出自动结束 thread.start() # 先返回202 Accepted,告知用户任务已启动 return jsonify({ "task_id": task_id, "status": "processing", "提示": "搜索任务已启动,可通过下方地址查询结果", "结果查询地址": f"/search/result/{task_id}" }), 202 @app.route("/search/result/<task_id>") def get_search_result(task_id): task = task_results.get(task_id) if not task: return jsonify({"error": "任务不存在"}), 404 if task["status"] == "processing": return jsonify({"status": "搜索中,请稍等"}), 200 else: # 返回结果后清理临时存储 result = task["result"] del task_results[task_id] return jsonify({"status": "completed", "result": result}), 200 if __name__ == "__main__": app.run(debug=True)
二、生产环境:用Celery异步任务队列实现
单线程方案在高并发或复杂部署场景下不稳定,推荐用Celery配合Redis/RabbitMQ做消息中间件,更可靠可控。
1. 安装依赖
pip install flask celery redis
2. 代码实现
from flask import Flask, jsonify, request from celery import Celery import uuid app = Flask(__name__) # 配置Celery连接Redis app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0' app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0' celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL']) celery.conf.update(app.config) @celery.task(bind=True) def long_running_search(self, query): # 模拟长时间搜索 import time time.sleep(10) return f"搜索关键词「{query}」找到100条匹配结果" @app.route("/search") def search(): query = request.args.get("q", "") if not query: return jsonify({"error": "请提供搜索关键词"}), 400 # 提交异步任务到Celery队列 task = long_running_search.delay(query) # 返回202 Accepted及任务查询信息 return jsonify({ "task_id": task.id, "status": "processing", "提示": "搜索任务已启动,可通过下方地址查询结果", "结果查询地址": f"/search/result/{task.id}" }), 202 @app.route("/search/result/<task_id>") def get_search_result(task_id): task = long_running_search.AsyncResult(task_id) if task.state == 'PENDING': return jsonify({"status": "搜索中,请稍等"}), 200 elif task.state == 'SUCCESS': return jsonify({"status": "completed", "result": task.result}), 200 else: return jsonify({"status": "failed", "提示": "搜索任务执行失败"}), 500 if __name__ == "__main__": app.run(debug=True)
关键说明
- 为什么不能直接在
search函数里先返回202再执行任务?因为Flask视图函数返回响应后,当前请求周期就结束了,后续代码不会被执行(不符合HTTP规范)。 - 轮询方式完全兼容Lynx这类不支持JS的浏览器,用户可以手动访问结果查询地址获取最终数据。
内容的提问来源于stack exchange,提问作者phao
相关产品推荐
相关产品推荐

