apache Kafka Replication设计分析

1.设计目标

提供可配置,需要保障强可用性可以enable这个功能,如果想要更高的效率而不太在乎数据丢失的话,可以disable这个功能
自动replica管理,当cluster发生变化时,即broker server增加或减少时,可以自动的管理和调整replicas

2.问题

  1. 如何将partition的replicas均匀的分配到各个broker servers上面?
  2. 如何进行replicas同步?

2.1 如何均匀的分配partition的replicas呢

例子如下:有15个partitions,5个brokers,做3 replicas
第一个replica怎么放,很简单,15/5,每个broker上依次放3个,如下图,012,345
然后再放其他replica的时候,思路如下:

  • a. 当一个broker down的时候,尽量可以把它的load分散到其他所有的broker上,从而避免造成单个broker的负担过重

    所以要考虑k,broker-0上的3个partition,0、1、2的第二个replica没有都放到broker-1,而是分别放到broker-1、2、3上

  • b. 当然一个partition的多个replica也不能放到同一个broker,那样就没有意义了 ,

考虑p0的3个replica分别放在broker-0、1、2上,注意这个分配的过程只会在初始化的时候做一次,并且一旦分配好后,会把结果存在zookeeper上,当cluster发生变化时不会重新分配,这样避免当增减broker时做大规模的数据迁移 当增减broker时,只会以最小的数据迁移来部分的replicas(随机选择m/n partitions迁移到Broker上)

这个方法的问题是, 没有考虑到partition和broker server的差异性,简单可用

broker-0 broker-1 broker-2 broker-3 broker-4 replica index
p0 p1 p2 p3 p4 (1st replica)
p5 p6 p7 p8 p9 (1st replica)
p4 p0 p1 p2 p3 (2nd replica)
p8 p9 p5 p6 p7 (2nd replica)
p3 p4 p0 p1 p2 (3nd replica)
p7 p8 p9 p5 p6 (3nd replica)

2.2 replica同步问题

支持同步和异步的方式
异步比较简单,leader存成功,就告诉client存成功,优势是低延迟,缺点是容易丢数据
同步即需要多个replica都存成功才告诉client存成功,缺点就是延迟比较长

在同步中,又需要考虑是否采用quorum-based的设计,或是采用all的设计
quorum-based的设计,活性比较强,延迟小些,问题是,至少要3-replics,并且要保证半数以上的replics是存活的
primary-backup的设计需要写所有的replicas,当然问题就是延迟比较长,而且一个慢节点会拖慢整个操作,好处就是比较简单,2-replicas也可以,只需要有一个replica是存活就ok

Kafka最终选择的是primary-backup方案,比较务实,作为balance
通过各种timeout来部分解决慢节点的问题
并且follower中message写到内存后就向leader发commit,而不等真正写到disk,来优化latency的问题

2.3 同步replication

同步方案可以容忍n-1 replica的失败,一个replica被选为leader,而其余的replicas作为followers
leader会维护in-sync replicas (ISR),follower replicas的列表,并且对于每个partition,leader和ISR信息都会存在zookeeper中

有些重要的offset需要解释一下,
log end offset (LEO),表示log中最后的message
high watermark (HW),表示已经被commited的message,HW以下的数据都是各个replicas间同步的,一致的。而以上的数据可能是脏数据,部分replica写成功,但最终失败了
flushed offset,前面说了为了效率message不是立刻被flush到disk的,而是periodically的flush到disk,所以这个offset表示哪些message是在disk上persisted的
这里需要注意的是,flushed offset有可能在HW的前面或后面,这个不一定

3. 写请求

client找到leader,写请求
leader写入本地log,然后每个followers通过socket channel获取更新,写入本地log,然后发送ack到leader ,leader发现已经收到所有follower发送的ack,表示message已经被committed,通知client,写成功
leader递增HW,并且定期广播HW到所有的followers,follower会定期去checkpoint HW数据,因为这个很重要,follower必须通过HW来判断那些数据是有效的(committed)

读请求

从leader读,注意只有HW下的数据会被读到,即只有committed过的数据会被读到

Broker失效场景

毫无疑问,这里需要考虑容错的问题
follower失败,很简单,leader可以直接把这个follower drop掉
当follower comeback的时候,需要truncate掉HW以上的数据,然后和leader同步,完成后,leader会把这个follower加会ISR

leader失败比较复杂一些,在写请求不同的阶段分为3种cases,
真正写数据前,简单,client重发
数据写完后,简单,直接选个新leader,继续
数据写入一半,这个有点麻烦,client会超时重发,如果保证在某些replica上,相同message不被写两次

当leader失败的时候,需要重新选一个leader,ISR里面所有followers都可以申请成为leader
依赖zookeeper的分布式锁,谁先register上,谁就是leader
新的leader会将它的LEO作为新的HW,其他的follower自然需要truncate,追赶leader

results matching ""

    No results matching ""