diff --git a/ch6-cloud/ch6-10-delay-job.md b/ch6-cloud/ch6-10-delay-job.md index 23865da..e113e7a 100644 --- a/ch6-cloud/ch6-10-delay-job.md +++ b/ch6-cloud/ch6-10-delay-job.md @@ -120,6 +120,68 @@ Go 自身的 timer 就是用时间堆来实现的,不过并没有使用二叉 ## rebalance 和幂等考量 -当我们的任务执行集群有机器故障时,需要对任务进行重新分配。按照之前的求模策略,对这台机器还没有处理的任务进行重新分配就比较麻烦了。如果是实际运行的线上系统,还要在故障时的任务平衡方面花更多的心思。比如将节点划分为协调节点和任务执行节点。由协调节点负责故障时的丢失任务平衡工作。 +当我们的任务执行集群有机器故障时,需要对任务进行重新分配。按照之前的求模策略,对这台机器还没有处理的任务进行重新分配就比较麻烦了。如果是实际运行的线上系统,还要在故障时的任务平衡方面花更多的心思。 + +下面给出一种思路: + +我们可以参考 elasticsearch 的设计,每份任务数据都有多个副本,这里假设两副本: + +``` +┌──────────┐ +│ node 1 │ +├──────────┴────────────────────────┐ +│ ┏━━━┓ ┌───┐ ┌───┐ ┏━━━┓ │ +│ ┃ 0 ┃ │ 1 │ │ 3 │ ┃ 4 ┃ │ +│ ┗━━━┛ └───┘ └───┘ ┗━━━┛ │ +└───────────────────────────────────┘ +┌──────────┐ +│ node 2 │ +├──────────┴────────────────────────┐ +│ ┌───┐ ┏━━━┓ ┏━━━┓ │ +│ │ 0 │ ┃ 2 ┃ ┃ 3 ┃ │ +│ └───┘ ┗━━━┛ ┗━━━┛ │ +└───────────────────────────────────┘ +┌──────────┐ +│ node 3 │ +├──────────┴────────────────────────┐ +│ ┏━━━┓ ┌───┐ ┌───┐ │ +│ ┃ 1 ┃ │ 2 │ │ 4 │ │ +│ ┗━━━┛ └───┘ └───┘ │ +└───────────────────────────────────┘ +``` + +一份数据虽然有两个持有者,但持有者持有的副本会进行区分,比如持有的是主副本还是非主副本,主副本在图中为摸黑部分,非主副本为正常线条。 + +一个任务只会在持有主副本的节点上被执行。 + +当有机器故障时,任务数据需要进行 rebalance 工作,比如 node 1 挂了: + +``` +┌──────────┐ +│ node 1 │ +├──────────┴────────────────────────┐ +│ │ +│ X X X │ +│ │ +└───────────────────────────────────┘ +┌──────────┐ +│ node 2 │ +├──────────┴────────────────────────┐ +│ ┌───┐ ┌───┐ ┏━━━┓ ┏━━━┓ ┏━━━┓ │ +│ │ 0 │ │ 1 │ ┃ 2 ┃ ┃ 3 ┃ ┃ 4 ┃ │ +│ └───┘ └───┘ ┗━━━┛ ┗━━━┛ ┗━━━┛ │ +└───────────────────────────────────┘ +┌──────────┐ +│ node 3 │ +├──────────┴────────────────────────┐ +│ ┏━━━┓ ┏━━━┓ ┌───┐ ┌───┐ ┌───┐ │ +│ ┃ 0 ┃ ┃ 1 ┃ │ 2 │ │ 3 │ │ 4 │ │ +│ ┗━━━┛ ┗━━━┛ └───┘ └───┘ └───┘ │ +└───────────────────────────────────┘ +``` + +node 1 的数据会被迁移到 node 2 和 node 3 上。 + +当然,也可以用稍微复杂一些的思路,比如对集群中的节点进行角色划分,由协调节点来做这种故障时的任务重新分配工作,考虑到高可用,协调节点可能也需要有 1 ~ 2 个备用节点以防不测。 之前提到我们会用 MQ 触发对用户的通知,在使用 MQ 时,很多 MQ 是不支持 exactly once 的语义的,这种情况下我们需要让用户自己来负责消息的去重或者消费的幂等处理。