Skip to content
zhongweikang edited this page Oct 9, 2022 · 13 revisions

通过提取shard和server维度的load数据,让shard在server上的分布更合理。

最早遇到这个问题是在调研mm2时,mm2作者基于kafka的group membership做到topic-partition-group粒度的任务在全集群等数量分配。感觉这个理念可以应用到我们内部系统上,所以做了LRMF,试图把rebalance和任务分配的机制抽离到sdk中,让分配规则能够自定义。

再次遇到这个,是团队做延迟队列,遇到分片在消费者上的分布问题。以及我们内部不同业务逻辑的任务,不能分布在同一个执行节点的问题。

翻facebook的技术blog偶然看到对于shard manager的描述,facebook通过9年的时间抽离独立的分片服务,迁移上百个业务场景到该服务,涉及到的container机器节点有10w+。基本能够确认这个想法的可实现性。

设计

client端:
client实现原理 server端:
server实现原理

fb在blog的描述是梗概,但思路和系统需要支持的能力阐述的比较清晰,我们需要做的就是在更细粒度上做技术决策,当前这篇文章作为整理决策思路的地方,可能会比较随意的记录思考过程。

shard移动是核心能力,触发shard移动的来源可能有两个:

  1. api,管理平台直接做的调整,允许人工干预
  2. core,核心决策引擎根据收集上来的load数据,在application维度计算出来shard和server的对应关系。这里的server指的是container,目前CNCF基本是标配,且能够简化我们的shard计算策略,如果是各种服务混部的场景,互相干扰,不能在application维度作出最好的决策。

shard manager部署在一套集群上,上面提到计算在fb内部设计上百个application,计算发生在app维度,触发计算的点有几个:

  1. server错误,例如:server机器挂了、网络不通,这些场景的先发现象就是heartbeat失败,报警出来,让op在管理平台作shard移动的决策。原因是,虽然网路不通但是很有可能还在工作,存在极端场景,shard本身的运转正常,只是与shard manager、etcd之间不能沟通,那么我们修复这个网络问题即可,不需要做shard移动,可能也不能通过shard manager做这个移动,只能停掉该container(走一下缩容和扩容的操作,也可以无差别做这个操作,杀错总比放过强)。
  2. shard scale
  3. shard和server的阈值监控是计算的关键来源,如果无差别周期计算所有,无效计算很多

shard manager本身该怎么构建,不同server之间怎么协作完成对几百个sharded application(一个公司,即便fb这种规模的公司也就是这个量级,非常规web application的应用在一个公司中大多处于基础服务层次,机器数量可能多,但应用类型会比较少)。

基于上面的假设,shard manager可以呈现下面几个形态:

  1. 单台机器竞争leader,启动时拿出所有机器节点当前的load进行计算,查看是否存在需要触发shard move的问题点;同时也需要watch load上报的etcd路径,运行时增量的处理实践。注意根据shard的拆分方式不同,监控维度需要定在shard的粒度,那么可能有千万级别的上报点(fb规模),这块单点成为瓶颈的风险非常大,这种规模,etcd存储的瓶颈(容量、读、写)都可能有问题。
  2. 多台机器在分配app的场景下,构建sharded application,走数量分布平均 -> 资源使用平均的过程,单server承载不了单app的场景和算法相关,暂时不考虑,看fb大概单app 10w+节点,问题不大。

app和负责机器的对应关系,提供api对接管理平台,如果预先对app应用场景的shard规模和单shard的cpu/mem/io等指标能有预估,那么在api这块直接对接策略做初次计算,计算结果写入etcd,更简单的处理方法是根据app和机器数量做整体平均分布的方法,然后走上面提到的shard上报load -> shard move的方法自动调整到合理,规模大了之后,采用预估/预计算。

采用上述2的方案,在watch load场景下,存在丢event的问题(逻辑处理失败,倒是该触发shard move,但没触发),丢掉大部分业务场景下问题不大,load上报本身就是有延迟,shard manager等待下次load上报并处理(如果还是处理失败,那么就是shard manager有问题,可以报警出来),如果遇到很严重的一个事件导致短短秒级就导致shard故障,这种就走heartbeat监听,把该shard移动到别的server,如果因为这个shard未统计的load峰值导致雪崩,那就最多挂两个shard(一个shard不能move多余1次,在1天之内,可以有这种阈值设定)。

单server通过watch etcd的路径,处理单app的所有load上报event,以及识别异常load,给出是否shard move的判断/报警/后台提示(可以人工操作)。我们团队面临的场景shard数量在100以内,完全不担心该问题。fb场景下上10w shard的上报流量,就会对etcd单集群造成一定压力,推测这种服务除了存储之外没有场景。

事件处理流程,watch app前缀,会收到所有heartbeat事件,shard move是根据app集群整体负载作出的决策,单纯一个shard的load异常只能作为触发shard move的点,不能认为只有这个shard会涉及到移动(可能是只有这个shard需要移动到别的server)。触发的决策是app全局考量下找更优解(这种shard的移动也要受限 -> 频繁的决策是否代表server整体负载临近阈值 -> 是否需要扩容)

针对波峰低谷时间跨度短的场景,这是比较少见的业务形态,涉及到定时收到压力的场景,会有这种,这种场景下,为了减少shard move,有几个方法:

  1. 业务形态无法变化,减少shard move对业务的影响,提升server整体负载承受能力,说白了就是扩容,提升机器对峰值上限的的认定上限
  2. 改变业务形态,把峰值利用随机、固定间隔这种策略削到阈值之下

只有处理完单app的shard move事件,才能处理进行下一个load异常事件的监测 + 计算 + 执行。和db中的snapshot类似,计算出来的计划一定是以当前app集群当前负载状态和shard分布为准。

shard和container的关系怎么建立?

在shard manager的集群上,有leader,leader负责管理sm自己的shard(app)和container的对应关系,上面聊过shard是通过admin api录入到sm,sm启动后会构建一次分配方案,如果已经有分配方案,且能和heartbeat中存活的container对上,那么就用现成的,进入监听load的状态。

分片会形成树状结构,在sm里面没有leader是做不到容错的,这里是单点问题。

应用接入sm,需要在sm申请secret,在注册shard的时候(通过api,或者管理平台)带上,防止租户之间互相干扰。