You need to enable JavaScript to run this app. 微服务无计数器并行计算:基于Google PubSub的任务完成判定方案
优惠活动
大模型
产品
解决方案
定价
更多

微服务无计数器并行计算:基于Google PubSub的任务完成判定方案

基于Google PubSub的并行子任务完成判定解决方案

背景

这套基于微服务架构的分布式系统可并行处理队列消息,包含两类应用:

  • A:REST API应用,负责接收各类处理请求。每个请求会被拆解为可独立支撑完整请求逻辑的子任务列表,A接收到新请求时,会向B应用的消息队列插入N条消息,N为动态值,随不同输入请求的逻辑复杂度变化。
  • B:队列处理应用,负责消费子任务消息完成业务逻辑。
    当同一请求对应的所有N条子任务均处理成功后,系统需向发起请求的用户告知整体处理结果。

原有初步方案

单个请求对应N个待处理子任务时,A会创建一个初始值为N的原子计数器,B每处理完一条队列消息,就将计数器值减1并读取最新值。最后一条消息处理完成后计数器值会归零,检测到计数器值为0时,由B向用户发送整体处理结果通知。

现存核心问题

  • 必须依赖原子计数器同步全局处理进度,缺少该组件则无法准确判定任务的实际完成时点
  • 包括Google PubSub在内的诸多分布式队列系统采用At Least Once投递策略,即同一条消息可能被多次取出重复处理,会导致尚未完成全部队列消息处理时,就提前向用户发送处理完成通知

正确落地方案

方案核心围绕「去原子计数器依赖」「幂等过滤重复消费」两个目标设计,完全适配Google PubSub的投递特性,具体实现逻辑如下:

1. 全局唯一标识绑定

给每个原始请求生成全局唯一的request_id,同时为同一请求下的所有子任务分配不重复的sub_task_id(可直接用1~N的顺序编号)。A在投递子任务消息时,必须将request_id、sub_task_id以及该请求的总子任务数N作为消息元数据,随子任务一起发送给B。
同时A可以提前在普通存储(KV/关系型数据库均可)中写入一条request_id的元数据,记录总子任务数N、当前状态为待处理,不需要初始化任何计数变量。

2. 子任务幂等处理

B消费到消息后,先做幂等校验:

  • 先查询当前(request_id, sub_task_id)是否已经标记为处理成功,可通过以下任意一种低成本存储实现:
    • 用Set结构存储:key为request_id,value为所有已完成的sub_task_id集合
    • 用关系型数据库表存储:字段包含request_id、sub_task_id,二者加联合唯一索引
  • 如果已标记为成功,直接对消息执行ack(确认消费),不触发任何后续逻辑,完全过滤重复投递的影响
  • 如果未标记为成功,先执行业务逻辑,处理成功后:
    • 若是Set结构,执行SADD命令将sub_task_id加入对应request_id的集合,因为Set的成员唯一性,重复执行也不会改变集合大小
    • 若是关系型数据库,直接插入该条记录,唯一索引会自动拦截重复插入的请求

3. 完成判定与通知

只有当子任务处理成功、且完成状态写入成功(即SADD返回1/数据库插入成功,说明该子任务是首次处理成功)时,才触发完成判定逻辑:

  • 查询当前request_id对应的总子任务数N
  • 统计该request_id下已完成的子任务数量(Set结构直接用SCARD取集合大小,数据库直接查询符合request_id的记录数)
  • 如果已完成数量等于N,说明所有子任务均处理完成,直接触发通知逻辑告知用户结果,同时将该request_id的状态标记为已完成,避免重复通知
  • 如果不等,直接结束流程,无额外操作

方案优势

  • 无强原子计数器依赖:不需要依赖专用的原子计数组件,普通的KV、关系型数据库即可支撑逻辑,存储选型灵活
  • 天然适配At Least Once投递策略:Set的唯一性、数据库的唯一索引自动过滤重复消费的影响,不会出现重复处理导致的计数不准问题
  • 性能开销极低:所有操作都是O(1)或单条记录查询的级别,不会因为子任务数量多产生额外性能压力

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.12 22:45:08