用途
本技能是面向 Web 应用异步处理的实现指南,用于在以下场景中参考:
- 设计某项处理是否应以异步方式实现时
- 选型与设计异步处理所需的基础设施时
- 实现需要异步处理的后端逻辑时
- 在前端实现异步处理的 UI 时
是否应采用异步处理的判断
当返回 HTTP 响应时处理无需已完成,可考虑采用异步处理。主要从以下角度判断:
- 用户体验(UX):处理耗时超过 1 秒,且希望避免从 UI 执行时画面无法操作的情况
- 外部依赖:调用外部 API 或服务等无法自行控制的对象时,担心对方系统故障或延迟导致性能下降、用户体验降低
- 即时性低:用户无需立即知道处理结果
- 资源保护:担心长时间处理占用 API 连接数,造成连接资源不必要的消耗
异步处理的实现方式
根据以下要素选择实现方式:托管 Web 应用的基础设施环境、Web 应用实现所用的框架与中间件、使用的云服务(AWS/Google Cloud)、期望的服务规模与所需健壮性、预算等。主要构建模式包括:
- Ruby on Rails 的 ActiveJob + ActionCable 实现
- Sidekiq 或 SolidQueue
- Rails 异步处理实现方式参见 @references/01_infrastructure.md
- TypeScript BullMQ 实现
- 基于 Redis 的高性能任务队列
- 最适合使用 Node.js/TypeScript 构建后端的情况
- 详见 @references/02_typescript_bullmq.md
- AWS SQS 任务队列 + AWS AppSync Events 事件发送 + DynamoDB 状态管理
- 以 AWS 无服务器架构实现
- Lambda + SQS + AppSync Events + DynamoDB 组合
- 详见 @references/03_aws_serverless.md
- Google Cloud Cloud Tasks 任务管理 + Firestore 通知与状态管理
- 以 Google Cloud 无服务器架构实现
- Cloud Run + Cloud Tasks + Firestore 组合
- 详见 @references/04_gcp_serverless.md
实现方式对比
| 观点 | Rails (Solid Queue) | Rails (Sidekiq) | BullMQ | AWS Serverless | GCP Serverless |
|---|---|---|---|---|---|
| 额外基础设施 | 无 | Redis | Redis | SQS、DynamoDB 等 | Cloud Tasks、Firestore |
| 可扩展性 | 中 | 高 | 高 | 非常高 | 非常高 |
| 运维负担 | 低 | 中 | 中 | 低 | 低 |
| 成本(低负载时) | 低 | 中 | 中 | 非常低 | 非常低 |
| 实时通知 | ActionCable | ActionCable | 自行实现 | AppSync Events | Firestore |
| 最大处理时间 | 无限制 | 无限制 | 无限制 | 15 分钟(Lambda) | 60 分钟(Cloud Run) |
| 适用用例 | 小〜中规模 Rails | 中〜大规模 Rails | Node.js 通用 | 以 AWS 为中心的无服务器 | 以 GCP 为中心的无服务器 |
异步处理的后端实现
后端实现时,请针对以下观点参考各实现方式的最佳实践。
共通实现观点:
- 参数接收方式
- 仅传递可序列化的数据
- 考虑参数大小限制(大数据使用外部存储)
- 推荐基于 ID 的引用
- 校验的实现
- 错误发生时的处理与通知
- 可重试错误与不可重试错误的分类
- 重试设置(次数、退避策略)
- Dead Letter Queue(DLQ)的处理
- 错误通知(结构化日志、外部服务联动)
- 进度状态管理
- 状态管理用数据模型的设计
- 进度率的计算与更新
- 实时通知(WebSocket、SSE、轮询)
- 取消处理
- 其他
- 确保幂等性
- 超时对策
- 任务拆分与链式
- 测试策略
实现方式别参考:
| 实现方式 | 参考 |
|---|---|
| Ruby on Rails (ActiveJob) | @references/05_backend_rails.md |
| TypeScript BullMQ | @references/06_backend_bullmq.md |
| AWS Serverless (Lambda + SQS) | @references/07_backend_aws.md |
| GCP Serverless (Cloud Run + Cloud Tasks) | @references/08_backend_gcp.md |
异步处理 UI 的前端实现
前端实现时,请针对以下观点参考各框架的最佳实践。
共通实现观点:
- 异步处理的调用方式
- 通过表单提交启动异步任务
- 通过自定义 Hook 或 Composable 管理调用
- 防止重复提交
- 状态管理与显示
- 实时显示进度状况
- 通过轮询更新状态
- 通过 WebSocket/SSE 推送通知
- Optimistic Updates(乐观更新)
- 连接恢复处理
- WebSocket/SSE 连接自动重连
- 页面隐藏时的连接管理
- 使用 Visibility API 进行状态同步
- 离线/在线状态监控
- 错误处理
- 通过 Error Boundary 捕获错误
- 全局错误处理
- 重试功能(Exponential Backoff)
- 向用户通知错误
框架别参考:
| 框架 | 参考 |
|---|---|
| Rails Hotwire (Turbo + Stimulus + ActionCable) | @references/09_frontend_hotwire.md |
| Next.js (React / App Router) | @references/10_frontend_nextjs.md |
| Vue.js 3 (Composition API) | @references/11_frontend_vuejs.md |
| React (Webpacker/Shakapacker) | @references/12_frontend_react.md |
生产环境部署
生产环境部署时的设置与最佳实践参见:
- 生产环境部署指南:@references/13_deployment.md
- Puma 插件设置
- ECS/Fargate 环境
- Kubernetes 环境
- CI/CD 设置
Solid Cable(ActionCable 的替代)
若希望无需 Redis 使用 ActionCable,可使用 Solid Cable。
- Solid Cable 参考:@references/14_solid_cable.md
- 设置方法
- 与 ActionCable 的区别
- 配置选项
故障排查
发生问题时参见:
- 故障排查指南:@references/15_troubleshooting.md
- 任务未执行时
- 状态未更新时
- WebSocket 连接问题
- 调试方法
环境隔离(共享 DB 时的注意事项)
当预发布环境与生产环境共享同一数据库时,需要以下设置。
Solid Queue 的队列名隔离:
# config/solid_queue.yml
staging:
workers:
- queues:
- <%= ENV.fetch('QUEUE_PREFIX', 'staging') %>_default
production:
workers:
- queues:
- <%= ENV.fetch('QUEUE_PREFIX', 'production') %>_default
# app/jobs/application_job.rb
class ApplicationJob < ActiveJob::Base
queue_as do
prefix = ENV.fetch('QUEUE_PREFIX', Rails.env)
"#{prefix}_default"
end
end
Solid Cable 的频道隔离:
# config/cable.yml
staging:
adapter: solid_cable
channel_prefix: <%= ENV.fetch('CABLE_PREFIX', 'staging') %>
production:
adapter: solid_cable
channel_prefix: <%= ENV.fetch('CABLE_PREFIX', 'production') %>
详见 @references/13_deployment.md 的「环境隔离设置」。