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

Flask应用中长耗时进程如何将向浏览器发送的请求数限制为1?

Flask长耗时任务处理方案(解决浏览器超时重发导致重复任务问题)

核心思路

  • 解耦HTTP请求生命周期与长耗时任务:将深度学习文件处理逻辑从Flask主进程剥离,交由异步任务队列托管,请求提交后立即返回任务ID,不会阻塞等待30分钟,从根源避免浏览器超时重发
  • 任务去重:相同文件/参数的未完成任务不会重复提交
  • 前端轮询查状态:前端拿到任务ID后定期主动查询进度,无需保持长连接

依赖安装

执行命令安装所需组件:
pip install flask celery redis
使用Celery作为异步任务队列,Redis作为消息中间件和任务状态存储,是Flask生态最成熟的长任务解决方案。

完整代码实现

1. 基础配置与初始化

from flask import Flask, request, jsonify
from celery import Celery
import hashlib
import os

app = Flask(__name__)
# Celery配置,Redis默认端口6379
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'

# 绑定Celery与Flask上下文
def make_celery(app):
    celery = Celery(
        app.import_name,
        broker=app.config['CELERY_BROKER_URL'],
        backend=app.config['CELERY_RESULT_BACKEND']
    )
    celery.conf.update(app.config)
    class ContextTask(celery.Task):
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return self.run(*args, **kwargs)
    celery.Task = ContextTask
    return celery

celery = make_celery(app)
# 任务去重缓存,生产环境建议存Redis避免Flask重启丢失
task_cache = {}

2. 定义长耗时处理任务

@celery.task(bind=True, time_limit=3600) # 设置1小时超时避免资源泄漏
def process_large_file_task(self, file_path, output_dir):
    # 替换为你的深度学习文件分割逻辑
    total_step = 100 # 按实际处理流程拆分步骤,用于计算进度
    for step in range(total_step):
        # 模拟30分钟处理时长
        import time
        time.sleep(18)
        # 更新任务进度,供前端查询
        self.update_state(state='PROGRESS', meta={'current': step+1, 'total': total_step})
    
    # 处理完成后返回结果
    output_file = os.path.join(output_dir, f"processed_{os.path.basename(file_path)}.zip")
    return {"download_url": f"/download/{os.path.basename(output_file)}"}

3. 接口实现(含去重逻辑)

@app.route('/submit-task', methods=['POST'])
def submit_task():
    # 计算上传文件哈希作为去重唯一标识
    uploaded_file = request.files['file']
    file_content = uploaded_file.read()
    file_hash = hashlib.md5(file_content).hexdigest()
    uploaded_file.seek(0) # 重置文件指针用于后续存储

    # 去重校验:相同文件的运行中任务直接返回已有ID
    if file_hash in task_cache:
        exist_task = process_large_file_task.AsyncResult(task_cache[file_hash])
        if exist_task.state in ['PENDING', 'PROGRESS']:
            return jsonify({
                "task_id": task_cache[file_hash],
                "msg": "该文件已在处理中"
            })
        else:
            # 已结束的任务清除缓存
            del task_cache[file_hash]
    
    # 存储上传文件,不要直接传递文件内容到Celery
    save_dir = "./upload"
    os.makedirs(save_dir, exist_ok=True)
    save_path = os.path.join(save_dir, f"{file_hash}_{uploaded_file.filename}")
    uploaded_file.save(save_path)

    # 提交异步任务
    output_dir = "./output"
    os.makedirs(output_dir, exist_ok=True)
    task = process_large_file_task.delay(save_path, output_dir)
    task_cache[file_hash] = task.id

    return jsonify({
        "task_id": task.id,
        "msg": "任务提交成功"
    })

@app.route('/task-status/<task_id>', methods=['GET'])
def get_task_status(task_id):
    task = process_large_file_task.AsyncResult(task_id)
    if task.state == 'PENDING':
        return jsonify({"state": "pending", "progress": 0})
    elif task.state == 'PROGRESS':
        progress = int(task.info['current'] / task.info['total'] * 100)
        return jsonify({"state": "progress", "progress": progress})
    elif task.state == 'SUCCESS':
        return jsonify({"state": "success", "result": task.result})
    else:
        return jsonify({"state": "failed", "error": str(task.info)})

服务启动步骤

  • 启动本地Redis服务(默认端口6379)
  • 启动Celery worker节点:celery -A app.celery worker --loglevel=info
  • 启动Flask主服务:flask run

前端侧配套修改

  • 提交任务后拿到task_id立即结束请求,不要设置长超时时间,避免触发浏览器超时重传机制
  • 用定时器每5~10秒调用/task-status/<task_id>接口查询进度,实时展示给用户
  • 可将task_id存在本地存储,页面刷新后可直接查询对应任务进度,无需重复上传

额外优化建议

  • 多用户场景下可在去重标识中加入用户ID,避免不同用户的相同文件互相影响
  • 任务缓存改用Redis存储,支持多实例部署且避免Flask重启后缓存丢失
  • 增加任务结果过期清理逻辑,避免存储冗余文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 10:36:04