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

Paramiko exec_command无法等待任务完成的Django Docker应用问题

解决Django异步任务未完成时视图提前读取文件的问题

问题本质

调用my_task.delay()后,异步任务是在后台独立执行的,但视图会立刻去读取还没生成/未完成的zip文件,自然会出问题。

可行解决方案

方案1:用Celery任务状态做前端轮询(推荐)

让前端先触发任务,再轮询任务状态,等任务完成后再请求下载文件,完全适配异步场景。

调整tasks.py

确保任务能正确反馈执行状态,并且等待SSH命令执行完毕再返回:

@shared_task(bind=True)
def my_task(self):
    # 去掉原命令里的until循环——zip/mv成功会直接退出,失败返回非0状态,不需要循环重试
    command2 = """sudo cp /.../ibt1.msh /.../ibt1.msh && \
    sudo zip -r result.zip ./output/ && \
    sudo mv result.zip /europlexusData/result.zip && \
    sudo rm -rf ./output
    """

    host2 = "你的目标容器地址"
    port = 22  # 替换为实际SSH端口
    username = "SSH用户名"
    password = "SSH密码"

    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh.connect(host2, port, username, password)
        # 执行命令并等待执行结束
        stdin, stdout, stderr = ssh.exec_command(command2, get_pty=True)
        exit_status = stdout.channel.recv_exit_status()
        
        if exit_status != 0:
            error_msg = stderr.read().decode()
            self.update_state(state='FAILURE', meta={'error': error_msg})
            raise Exception(f"命令执行失败: {error_msg}")
        
        return {'status': 'success', 'file_path': '/europlexusData/result.zip'}
    except Exception as e:
        self.update_state(state='FAILURE', meta={'error': str(e)})
        raise
    finally:
        ssh.close()

新增视图逻辑

拆分出三个视图:触发任务、检查任务状态、下载文件:

from django.http import JsonResponse, FileResponse
from .tasks import my_task
import os

def trigger_task(request):
    task = my_task.delay()
    return JsonResponse({'task_id': task.id})

def check_task_status(request, task_id):
    task = my_task.AsyncResult(task_id)
    if task.state == 'SUCCESS':
        return JsonResponse({
            'state': task.state,
            'ready': True
        })
    elif task.state == 'FAILURE':
        return JsonResponse({
            'state': task.state,
            'error': task.result.get('error'),
            'ready': False
        })
    else:
        return JsonResponse({'state': task.state, 'ready': False})

def download_result(request):
    file_path = '/europlexusData/result.zip'
    if os.path.exists(file_path):
        return FileResponse(open(file_path, 'rb'), filename='result.zip')
    else:
        return JsonResponse({'error': '文件尚未生成'}, status=404)

前端配合逻辑

先调用trigger_task拿到task_id,然后每隔几秒请求check_task_status,直到返回ready: True,再调用download_result下载文件。

方案2:视图同步等待任务完成(不推荐)

如果非要在同一个请求里完成,可以让视图等待任务结束,但会阻塞Django进程,不适合高并发场景:

from django.http import FileResponse
from .tasks import my_task
import os

def my_view(request):
    task = my_task.delay()
    # 设置超时时间,比如5分钟
    result = task.get(timeout=300)
    file_path = '/europlexusData/result.zip'
    
    if os.path.exists(file_path):
        return FileResponse(open(file_path, 'rb'), filename='result.zip')
    else:
        return JsonResponse({'error': '任务执行失败,文件未生成'}, status=500)

方案3:WebSocket主动通知(进阶)

用Django Channels实现WebSocket,任务完成后主动推送给前端,让用户立刻知道可以下载,体验更好。

额外优化点

  • 尽量避免在SSH命令里用sudo,可以给SSH用户配置对应目录的权限,或者用有权限的用户连接。
  • 给生成的zip文件加唯一标识(比如任务ID),防止多任务同时执行时文件被覆盖。
  • 给任务添加日志记录,方便排查执行异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 06:31:21