Wetask 的下一次更新将带来持久、可围栏的外部 Worker、原子化批量结算,以及原生 Go、TypeScript 和 Python 客户端。

Wetask 最初的简单目标是:把通常围绕后台任务系统组装的组件合并到一个基于 Go 的运行时中。

这意味着一个任务队列、调度器、缓存、共识、运维 API 和可观测性,而无需为每次部署都单独准备 broker、结果后端和调度服务。

Wetask 的下一次更新将把这一理念扩展到嵌入式处理器之外。应用程序将能够在另一个进程、容器、语言或执行环境中运行自己的 Worker,而 Wetask 仍负责持久化的任务状态。

自带 Worker

当任务执行无法或不应该发生在 Wetask 服务器内部时,外部 Worker 非常有用:

  • 运行机器学习任务的 Python Worker
  • 调用应用服务的 TypeScript Worker
  • 处理高吞吐量后端工作的 Go Worker
  • GPU 或内存专用 Worker 池
  • 拥有独立发布周期的独立部署 Worker
  • 与队列和控制平面进程隔离的 Worker

原生 Go、TypeScript 和 Python 客户端将公开 Worker API,而 HTTP 和 gRPC 仍可用于自定义集成。

Worker 可以按队列和任务类型请求批次、续期长时间运行的工作、报告成功,或将失败归类为瞬时、永久或已取消。凭证可被限制在特定队列和任务类型上。

围栏比另一个 Ack 端点更重要

至少一次交付的系统必须假定任务可能再次被投递。Worker 可能暂停、失去连接、超出租约,并在另一个 Worker 已收到同一任务后继续运行。

Wetask 为每次认领的尝试分配一次性围栏令牌。只有当任务、认领、令牌、主体、尝试和协调器仍然匹配时,才接受完成。一旦尝试过期或被取代,旧 Worker 就无法完成它。

令牌本身仅返回给 Worker,Wetask 存储其 SHA-256 哈希值。

这并不能使外部副作用恰好执行一次。Worker 在确认任务前对卡片扣费并崩溃,仍可能导致重试。处理器必须使用任务 ID 和尝试次数,使下游操作具有幂等性。

围栏所能保证的是:过时的 Worker 无法覆盖当前尝试的状态。

持久且幂等的结算

成功和失败的结算会在 Wetask 返回回执之前持久化。重试相同的确认将返回原始回执,而不是将任务完成或重试两次。

失败处理是任务感知的:

  • transient:在仍允许重试时安排另一次尝试
  • permanent:结束任务并将其记录到死信队列
  • cancelled:不重试,直接结束执行
  • 租约过期时自动应用已配置的重试和 DLQ 策略

Worker 可以原子化地确认或拒绝一个完整批次。Wetask 会先验证每个围栏。若其中一项已过期或无效,则该批次中所有未结算的任务都不会被更改。

批量结算还通过一次 WAL 同步记录批次,降低了持久化存储开销。

基准测试

我们对完整的进程内生命周期进行了基准测试:

  1. 提交任务
  2. 作为外部 Worker 认领任务
  3. 确认任务

该基准不执行应用工作,也不包含 HTTP 或 gRPC 网络开销。它仅测量任务运行时本身。

环境:

  • Apple M3
  • macOS darwin/arm64
  • Go 基准进程报告 8 路并行
  • 批次大小分别为 1、8 和 32
  • 每个场景采样三次
  • 三秒基准测试窗口

命令:

go test ./internal/task \
  -run '^$' \
  -bench '^BenchmarkWorkerClaimAck($|Parallel$)' \
  -benchmem \
  -benchtime=3s \
  -count=3

Enter fullscreen mode Exit fullscreen mode

内存生命周期

下表报告每秒任务数的中位数:

场景 早期基线 当前结果 差异
批次 1 74,933 77,123 +2.9%
批次 8 84,434 86,393 +2.3%
批次 32 84,661 86,783 +2.5%
并行单任务操作 64,429 63,443 -1.5%

批次 32 完成完整生命周期约需 11.52 微秒/任务。

内存模式的提升是有意保持适度的。没有磁盘屏障需要摊销,且每个任务仍需 ID、认领令牌、状态转换、队列记账、结果保留、指标和生命周期事件。并行结果基本持平,因此一并展示。

持久生命周期

持久模式在接受提交、认领和结算时包含 WAL 同步:

场景 中位吞吐量 相对于早期 86.34 tasks/s 基线
批次 1 80.08 tasks/s 0.93x
批次 8 645.7 tasks/s 7.5x
批次 32 2,402 tasks/s 27.8x
并行单任务操作 136.4 tasks/s 1.58x

在批次 32 下,持久化处理每任务约耗时 416 微秒

理论批处理上限为 32 倍,因为相同的持久化屏障被 32 个任务共享。实测 27.8 倍提升达到该上限的约 87%。剩余时间用于 WAL 序列化、任务状态、指标及磁盘同步本身。

这些仅为 Wetask 内部测量,并非声称 Wetask 比所有 broker 更快。公平的竞争性基准必须使用相同的负载、传输、批次大小、持久化策略、复制和确认语义。

Wetask 与其他系统的比较

RabbitMQ 仲裁队列

RabbitMQ 仲裁队列是成熟的、基于 Raft 复制的队列,专为数据安全和领导者故障转移而设计。发布者确认和手动消费者确认提供了坚实的生产基础。

RabbitMQ 在复制队列持久性、运维历史、协议支持和生态规模上仍领先于 Wetask。

Wetask 追求更面向任务的体验:结果、重试、截止时间、取消、死信、调度、缓存功能均通过单一运行时和 API 提供。

参考:
RabbitMQ 仲裁队列

NATS JetStream

JetStream 是最接近的协议级对比。它提供拉取和推送消费者、显式 Ack、Nak、延迟 Nak、Term、进行中确认、AckWait、退避和最大投递次数。

JetStream 在集群流存储、发布/订阅、复制和高吞吐量消息传递方面显著更成熟。

Wetask 的区别在于严格的尝试围栏。JetStream 文档指出,晚到的确认在消息已被重新投递给另一个订阅者后仍可能被接受。Wetask 则拒绝来自已被取代尝试的结算。

参考:
NATS JetStream 消费者

AWS SQS 与 Google Cloud Pub/Sub

托管队列免除了大部分 broker 运维,并提供弹性、多可用区基础设施。

SQS 使用可见性超时和回执句柄。标准队列仍为至少一次投递,消费者必须容忍重复投递。

Google Cloud Pub/Sub 的恰好一次拉取订阅提供了一个特别有趣的对比:重新投递后,只有最新的确认 ID 仍然有效。这在概念上接近 Wetask 的围栏模型。

Wetask 适合需要自托管、可移植性、本地部署和任务特定状态的团队。当托管服务和由提供商运维的可用性更重要时,云队列是更优选择。

参考:

Apache Kafka

Kafka 是一个有序、可重放的事件日志。它更适合事件流、大型保留历史、分区顺序和多个独立消费者组。

传统 Kafka 消费者提交分区偏移量,而非独立结算任意作业。单个记录的重试可能会影响分区进度,而任务结果、每任务租约和死信行为通常需要应用约定或额外主题。

Wetask 是后台作业的更直接模型。Kafka 则是持久化事件流的更强模型。

参考:
Kafka 投递语义

Celery 与 RabbitMQ

Celery 仍是 Python 应用的成熟选择。它拥有庞大的生态、熟悉的任务装饰器、重试、Canvas 工作流以及多年的运维经验。

其典型生产形态结合 Celery Worker、RabbitMQ 等 broker、结果后端以及 Celery Beat 用于调度。

Wetask 的主张是更小的运维面:一个 Go 运行时提供队列、任务状态、调度器、缓存、API 和管理,而外部 Worker 仍可用 Python、TypeScript 或 Go 编写。

参考:
Celery 文档

Temporal

Temporal 解决的问题比任务队列更大。它专为跨进程故障和基础设施中断仍能恢复的持久化多步骤工作流而设计。

Temporal 更适合长时间运行的业务流程、Saga、持久化定时器和工作流历史。Wetask 则有意保持简洁,面向普通异步作业、计划任务和应用缓存。

参考:
Temporal 文档

Wetask 的适用场景

Wetask 并不试图取代所有消息系统。

当日志即产品时选择 Kafka;当持久化工作流编排即产品时选择 Temporal;当优先外包运维时选择托管云队列;当成熟的复制消息基础比集成应用运行时更重要时选择 RabbitMQ 或 JetStream。

Wetask 适合希望获得以下特性的团队:

  • 紧凑的自托管任务平台
  • 持久化后台作业
  • 服务器进程之外的 Worker
  • 严格的过时 Worker 拒绝机制
  • 任务结果、重试、截止时间和 DLQ 管理
  • 无需额外服务栈即可获得调度和缓存能力
  • HTTP、gRPC、Go、TypeScript 和 Python 集成

可用性

外部 Worker 计划在下一次 Wetask Alpha 版本中发布。首个版本将专注于持久化单节点使用和受控生产试点。多节点协调已存在,但任务队列 WAL 所有权目前为每节点;复制任务所有权和永久节点丢失恢复仍为未来强化工作。

这一边界是刻意为之。Alpha 版本的目标是将完整的 Worker 体验带入真实项目,发布可重现的证据,并让生产反馈塑造下一阶段。

如果您正在评估后台作业基础设施,最有用的反馈不是“哪个基准数字最大?”,而是:哪些失败语义、运维模型和 Worker 体验与您实际需要运行的系统相匹配?