基于Django REST Framework实现异步长请求处理需求
处理长耗时请求:立即返回200+异步返回结果的实现方案
核心逻辑
先返回200 OK确认请求已接收,后台异步执行长任务,后续通过回调、轮询或推送方式把结果给到客户端。
1. 回调URL机制
- 客户端发起请求时,携带一个自身可接收请求的回调URL
- 服务端收到请求后立刻返回200响应,同时启动异步线程处理任务
- 任务完成后,服务端主动调用回调URL,把结果发送过去
- 伪代码示例(Python):
from fastapi import FastAPI import asyncio import requests app = FastAPI() # 接收长任务请求 @app.post("/long-task") async def accept_task(callback_url: str): # 启动异步任务 asyncio.create_task(handle_long_task(callback_url)) return {"status": "accepted", "msg": "任务已启动,完成后将回调通知"} # 异步处理长任务 async def handle_long_task(callback_url): # 模拟3分钟以上的耗时操作 await asyncio.sleep(190) task_result = {"data": "长任务处理完成的结果"} # 调用回调URL推送结果 requests.post(callback_url, json=task_result)
2. 轮询查询机制
- 服务端收到请求后生成唯一任务ID,返回200的同时把ID给到客户端
- 客户端拿到ID后,定期调用查询接口获取任务状态
- 任务完成时,查询接口直接返回处理结果
- 伪代码示例(Java):
import org.springframework.web.bind.annotation.*; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @RestController public class LongTaskController { private final ExecutorService executor = Executors.newFixedThreadPool(5); private final Map<String, String> taskResults = new ConcurrentHashMap<>(); // 启动长任务 @PostMapping("/long-task") public Map<String, String> startTask() { String taskId = UUID.randomUUID().toString(); executor.submit(() -> { // 模拟长耗时操作 try { Thread.sleep(190000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } taskResults.put(taskId, "长任务处理完成的结果"); }); return Map.of("taskId", taskId, "status", "running"); } // 查询任务结果 @GetMapping("/task-result/{taskId}") public Map<String, Object> getResult(@PathVariable String taskId) { if (!taskResults.containsKey(taskId)) { return Map.of("status", "pending"); } return Map.of("status", "completed", "result", taskResults.get(taskId)); } }
3. WebSocket实时推送
- 客户端先与服务端建立WebSocket连接
- 发送长任务请求后,服务端立即返回200,同时通过WebSocket告知任务启动
- 任务完成后,直接通过WebSocket把结果推送给客户端
- 伪代码示例(前端JS):
// 建立WebSocket连接 const socket = new WebSocket('ws://your-server-domain/ws'); socket.onopen = () => { // 发送长任务请求 fetch('/long-task', { method: 'POST' }) .then(res => res.json()) .then(data => console.log(data.msg)); }; // 接收服务端推送的结果 socket.onmessage = (event) => { const message = JSON.parse(event.data); if (message.type === 'task_done') { console.log('任务结果:', message.result); socket.close(); } };
关键注意点
- 异步任务状态要持久化,比如存数据库或缓存,避免服务重启丢失任务信息
- 要处理任务失败场景,及时通过对应渠道通知客户端错误详情
- 回调URL需保证客户端能稳定接收,必要时加重试机制
- 轮询间隔要合理,避免过短增加服务端压力,过长影响体验
内容的提问来源于stack exchange,提问作者Silverwing
相关产品推荐
相关产品推荐

