异步任务怎么设计:轮询/WebSocket/回调(附PRD写法)

前言

异步任务是处理耗时操作的常用方案。很多用户体验问题都是因为异步任务设计不当:导出等半天没反应、不知道进度、失败了也不知道。这篇给你3种异步任务设计方案。

一、3种异步任务方案对比

方案 原理 适用场景 优点 缺点
轮询 定时查询任务状态 简单场景 实现简单 浪费资源
WebSocket 服务端主动推送 实时性要求高 实时 复杂度高
回调 任务完成后回调接口 第三方服务 解耦 需要公网地址

二、轮询方案

PRD写法

复制代码
功能:数据导出
异步方案:轮询
流程:
1. 用户点击"导出"
2. 后端创建导出任务,返回task_id
3. 前端每3秒调用接口查询任务状态
4. 任务完成后,显示下载链接
5. 用户点击下载

接口:查询任务状态
请求:GET /api/tasks/{task_id}
响应:
{
  "status": "processing",  // 状态:pending/processing/success/failed
  "progress": 50,          // 进度:0-100
  "message": "正在导出第500条",
  "download_url": null     // 下载链接(成功时返回)
}

轮询规则:
- 间隔:3秒
- 超时:5分钟(超时后停止轮询,提示用户刷新)
- 失败:显示错误信息,停止轮询

三、WebSocket方案

PRD写法

复制代码
功能:数据导出
异步方案:WebSocket
流程:
1. 用户打开页面,建立WebSocket连接
2. 用户点击"导出"
3. 后端创建导出任务
4. 后端通过WebSocket推送进度
5. 任务完成后,推送下载链接
6. 用户点击下载

WebSocket消息格式:
{
  "type": "task_progress",  // 消息类型
  "task_id": "123",
  "status": "processing",
  "progress": 50,
  "message": "正在导出第500条"
}

{
  "type": "task_complete",
  "task_id": "123",
  "status": "success",
  "download_url": "https://xxx.com/file.xlsx"
}

四、回调方案

PRD写法

复制代码
功能:支付回调
异步方案:回调
流程:
1. 用户发起支付
2. 后端调用支付平台接口
3. 支付平台返回支付链接
4. 用户完成支付
5. 支付平台回调我方接口
6. 我方更新订单状态
7. 前端轮询订单状态(或WebSocket推送)

回调接口:POST /api/payment/callback
请求参数:
{
  "order_no": "ORD123",
  "trade_no": "PAY456",
  "status": "success",
  "amount": 199.00,
  "sign": "xxx"  // 签名
}

响应:
{
  "code": 200,
  "message": "success"
}

安全要求:
- 验证签名
- 验证订单状态(防止重复回调)
- 记录回调日志

五、异步任务设计实施步骤

步骤1:分析业务场景

在设计异步任务前,需要分析业务场景:

  1. **识别耗时操作:**哪些操作需要较长时间(>3秒)
  2. **分析用户需求:**用户是否需要实时看到进度
  3. **评估实时性要求:**对实时性的要求有多高
  4. **考虑技术成本:**不同方案的技术实现成本

在梳理异步任务设计方案时,可以使用思维导图工具来整理不同场景和方案的对应关系,比如使用AI思维导图工具,输入业务场景和技术要求,自动生成结构化的异步任务设计思维导图,帮助快速确定最适合的方案。

步骤2:选择异步方案

根据业务场景,选择合适的异步方案:

  • **轮询:**适用于简单场景,实时性要求不高
  • **WebSocket:**适用于实时性要求高的场景
  • **回调:**适用于第三方服务场景

步骤3:设计任务状态

设计任务状态流转:

  • **状态定义:**pending(待处理)、processing(处理中)、success(成功)、failed(失败)
  • **状态流转:**明确状态之间的转换规则
  • **进度信息:**设计进度字段(0-100)和进度消息

步骤4:实现任务管理

实现任务管理功能:

  • **任务创建:**创建任务,返回task_id
  • **任务查询:**提供查询任务状态的接口
  • **任务取消:**支持取消正在执行的任务
  • **任务重试:**支持失败任务的重试

六、实际案例详解

案例1:电商数据导出(轮询方案)

**业务场景:**用户需要导出大量订单数据(10万条),导出需要5-10分钟。

**方案选择:**轮询方案(简单场景,实时性要求不高)

实现方案:

复制代码
1. 前端实现:
   - 用户点击"导出"按钮
   - 调用创建任务接口:POST /api/export/orders
   - 获取task_id
   - 每3秒调用查询接口:GET /api/tasks/{task_id}
   - 显示进度条和进度信息
   - 任务完成后,显示下载链接

2. 后端实现:
   - 创建导出任务,返回task_id
   - 异步执行导出任务(使用队列)
   - 更新任务状态和进度
   - 生成Excel文件,上传到OSS
   - 更新任务状态为success,返回下载链接

3. 数据库设计:
   CREATE TABLE tasks (
     id BIGINT PRIMARY KEY,
     user_id BIGINT,
     type VARCHAR(50),  -- export/import/process
     status VARCHAR(20),  -- pending/processing/success/failed
     progress INT DEFAULT 0,  -- 0-100
     message TEXT,
     result_url VARCHAR(500),  -- 结果文件URL
     error_message TEXT,
     created_at DATETIME,
     updated_at DATETIME
   );

4. 接口设计:
   POST /api/export/orders
   请求:{ "start_date": "2023-01-01", "end_date": "2023-12-31" }
   响应:{ "task_id": "123", "message": "任务已创建" }
   
   GET /api/tasks/{task_id}
   响应:{
     "status": "processing",
     "progress": 50,
     "message": "正在导出第50000条",
     "download_url": null
   }

5. 轮询实现(JavaScript):
   const pollTaskStatus = async (taskId) => {
     const maxAttempts = 100; // 最多轮询100次(5分钟)
     let attempts = 0;
     
     const poll = async () => {
       if (attempts >= maxAttempts) {
         alert('导出超时,请刷新页面重试');
         return;
       }
       
       const response = await fetch(`/api/tasks/${taskId}`);
       const data = await response.json();
       
       updateProgress(data.progress, data.message);
       
       if (data.status === 'success') {
         showDownloadLink(data.download_url);
       } else if (data.status === 'failed') {
         showError(data.error_message);
       } else {
         attempts++;
         setTimeout(poll, 3000); // 3秒后再次查询
       }
     };
     
     poll();
   };

**效果:**用户点击导出后,可以看到实时进度,任务完成后自动显示下载链接,体验良好。

案例2:实时数据处理(WebSocket方案)

**业务场景:**用户上传文件后,需要实时看到文件处理进度(解析、验证、导入)。

**方案选择:**WebSocket方案(实时性要求高)

实现方案:

复制代码
1. 前端实现:
   - 用户上传文件
   - 建立WebSocket连接
   - 发送任务创建消息
   - 接收进度推送消息
   - 更新进度条
   - 任务完成后,显示结果

2. 后端实现:
   - 接收文件上传
   - 创建处理任务
   - 建立WebSocket连接(存储连接信息)
   - 异步处理文件(使用队列)
   - 处理过程中,通过WebSocket推送进度
   - 处理完成后,推送结果

3. WebSocket消息格式:
   客户端 -> 服务端:
   {
     "type": "create_task",
     "file_id": "123"
   }
   
   服务端 -> 客户端:
   {
     "type": "task_progress",
     "task_id": "456",
     "status": "processing",
     "progress": 50,
     "message": "正在解析第1000行"
   }
   
   {
     "type": "task_complete",
     "task_id": "456",
     "status": "success",
     "result": { "total": 10000, "success": 9800, "failed": 200 }
   }

4. 连接管理:
   - 使用Redis存储WebSocket连接(key: user_id, value: connection_id)
   - 连接断开时,清理连接信息
   - 支持自动重连(最多3次)
   - 重连失败后,降级为轮询

**效果:**用户可以看到实时的处理进度,体验流畅,无需手动刷新。

案例3:支付回调(回调方案)

**业务场景:**用户支付完成后,支付平台回调我方接口更新订单状态。

**方案选择:**回调方案(第三方服务场景)

实现方案:

复制代码
1. 支付流程:
   - 用户发起支付
   - 后端调用支付平台接口,创建支付订单
   - 返回支付链接给前端
   - 用户完成支付
   - 支付平台回调我方接口
   - 我方更新订单状态
   - 前端轮询订单状态(或WebSocket推送)

2. 回调接口设计:
   POST /api/payment/callback
   请求参数:
   {
     "order_no": "ORD202312010001",
     "trade_no": "PAY202312010001",
     "status": "success",  // success/failed
     "amount": 199.00,
     "pay_time": "2023-12-01 10:00:00",
     "sign": "xxx"  // 签名
   }
   
   响应:
   {
     "code": 200,
     "message": "success"
   }

3. 安全验证:
   - 验证签名(防止伪造)
   - 验证订单状态(防止重复回调)
   - 验证金额(防止金额篡改)
   - 记录回调日志(便于排查问题)

4. 幂等处理:
   - 使用订单号 + 交易号作为唯一标识
   - 如果订单已处理,直接返回成功
   - 使用数据库事务保证原子性

5. 重试机制:
   - 支付平台会重试(通常3次,间隔递增)
   - 我方需要支持幂等,避免重复处理
   - 如果回调失败,支付平台会继续重试

**效果:**支付完成后,订单状态自动更新,用户无需手动刷新,体验流畅。

七、常见错误及解决方案

错误1:轮询间隔设置不合理

**表现:**轮询间隔太短,浪费服务器资源;间隔太长,用户等待时间长。

**示例:**轮询间隔设置为1秒,导致服务器压力大。

解决方案:

  • 根据任务类型设置合理的间隔:简单任务3-5秒,复杂任务5-10秒
  • 支持动态调整:任务开始时间隔短,接近完成时间隔长
  • 设置最大轮询次数,避免无限轮询

错误2:WebSocket连接管理不当

**表现:**连接断开后没有重连,用户看不到进度;连接泄漏,占用服务器资源。

**示例:**用户刷新页面后,WebSocket连接断开,但没有重连。

解决方案:

  • 实现自动重连机制(最多3次)
  • 重连失败后,降级为轮询
  • 页面关闭时,清理连接
  • 使用心跳机制,检测连接状态

错误3:回调接口未做幂等处理

**表现:**支付平台重复回调,导致订单重复处理。

**示例:**支付平台重试3次,订单状态被更新3次。

解决方案:

  • 使用订单号 + 交易号作为唯一标识
  • 如果订单已处理,直接返回成功
  • 使用数据库事务保证原子性
  • 记录回调日志,便于排查问题

错误4:任务状态更新不及时

**表现:**任务已完成,但状态没有及时更新,用户看不到结果。

**示例:**导出任务已完成,但前端还在轮询。

解决方案:

  • 任务完成后,立即更新状态
  • 使用消息队列,确保状态更新可靠
  • 定期检查任务状态,处理异常任务

错误5:未处理任务失败情况

**表现:**任务失败后,用户不知道原因,无法重试。

**示例:**导出任务失败,但前端只显示"失败",没有错误信息。

解决方案:

  • 记录详细的错误信息
  • 前端显示错误信息,提示用户重试
  • 支持任务重试功能
  • 提供客服联系方式,便于用户反馈

八、最佳实践

实践1:根据场景选择方案

根据业务场景选择合适的异步方案:

  • **简单场景:**使用轮询,实现简单,成本低
  • **实时性要求高:**使用WebSocket,体验好
  • **第三方服务:**使用回调,解耦
  • **混合方案:**WebSocket + 轮询降级,保证可靠性

在选择方案时,可以使用思维导图工具来整理不同场景和方案的对应关系,比如使用AI思维导图工具,输入业务场景和技术要求,自动生成结构化的异步任务设计思维导图,帮助快速确定最适合的方案。

实践2:设计完善的任务状态

设计完善的任务状态流转:

  • **状态定义:**pending、processing、success、failed、cancelled
  • **进度信息:**进度百分比(0-100)、进度消息
  • **结果信息:**成功时返回结果URL,失败时返回错误信息
  • **时间信息:**创建时间、开始时间、完成时间

实践3:实现可靠的任务管理

实现可靠的任务管理功能:

  • **任务持久化:**任务信息存储在数据库中
  • **任务队列:**使用消息队列处理任务,保证可靠性
  • **任务监控:**监控任务执行情况,及时发现问题
  • **任务清理:**定期清理过期任务,释放存储空间

实践4:优化用户体验

优化用户体验,提升满意度:

  • **进度显示:**显示进度条和进度信息,让用户知道进度
  • **错误提示:**任务失败时,显示详细的错误信息
  • **重试功能:**支持任务重试,减少用户操作
  • **超时处理:**设置超时时间,避免用户无限等待

实践5:保证系统稳定性

保证系统稳定性,避免影响用户体验:

  • **限流保护:**限制轮询频率,避免服务器压力过大
  • **连接管理:**合理管理WebSocket连接,避免连接泄漏
  • **异常处理:**完善的异常处理机制,保证系统稳定
  • **监控告警:**监控任务执行情况,及时发现问题

九、FAQ

Q1:轮询间隔多久合适?

建议:3-5秒。太短浪费资源,太长用户等待时间长。可以根据任务类型动态调整:简单任务3秒,复杂任务5秒。

Q2:WebSocket断开怎么办?

建议:自动重连(最多3次),重连失败后降级为轮询。使用心跳机制检测连接状态,及时发现问题。

Q3:回调失败怎么办?

建议:支付平台会重试(通常3次),我方需要做幂等处理。如果回调失败,可以主动查询订单状态,确保数据一致性。

Q4:如何快速设计异步任务方案?

建议:先分析业务场景,确定实时性要求,然后选择合适的方案。可以使用思维导图工具来整理不同场景和方案的对应关系,比如使用AI思维导图工具,输入业务场景和技术要求,自动生成结构化的异步任务设计思维导图,帮助快速确定最适合的方案。

Q5:任务状态如何设计?

建议:定义pending、processing、success、failed、cancelled等状态。记录进度信息(0-100)、进度消息、结果信息、时间信息等。

Q6:如何保证任务可靠性?

建议:使用消息队列处理任务,保证可靠性。任务信息存储在数据库中,支持任务重试。定期检查任务状态,处理异常任务。

Q7:如何优化用户体验?

建议:显示进度条和进度信息,让用户知道进度。任务失败时,显示详细的错误信息。支持任务重试,减少用户操作。设置超时时间,避免用户无限等待。

相关推荐
Deepoch14 小时前
赋能未来:Deepoc具身模型开发板如何成为机器人创新的“基石”
人工智能·机器人·开发板·具身模型·deepoc
Tao____14 小时前
基于Ruoyi开发的IOT物联网平台
java·网络·物联网·mqtt·网络协议
格林威14 小时前
传送带上运动模糊图像复原:提升动态成像清晰度的 6 个核心方案,附 OpenCV+Halcon 实战代码!
人工智能·opencv·机器学习·计算机视觉·ai·halcon·工业相机
且去填词14 小时前
DeepSeek API 深度解析:从流式输出、Function Calling 到构建拥有“手脚”的 AI 应用
人工智能·python·语言模型·llm·agent·deepseek
九河云14 小时前
从“被动适配”到“主动重构”:企业数字化转型的底层逻辑
大数据·人工智能·安全·重构·数字化转型
Java猿_14 小时前
使用Three.js创建交互式3D地球模型
人工智能·语言模型·自然语言处理
FL1717131414 小时前
excel转latex
人工智能
Aurora-Borealis.15 小时前
Day27 机器学习流水线
人工智能·机器学习