先说卡夫卡吧。估计没人看Hbase,就略过,说说大家喜欢的吧。1.Kafka架构图在一个Kafka架构中,有多个Producer,多个Broker,多个Consumer。每个Producer可以对应多个Topic,每个Consumer只能对应一个ConsumerGroup。整个Kafka架构对应一个ZK集群,ZK通过该集群管理集群配置,选举Leader,并在consumergroup发生变化时进行rebalance。对于一个复杂的分布式系统,没有丰富的经验和强大的架构能力,是很难做到系统简单易维护的。众所周知,在一个软件的生命周期中,后期维护占到了70%,所以系统的可维护性是极其重要的。Kafka能够成为大数据领域事实上的标准,很大程度上是因为它的运维非常方便简单。今天我们就来看看Kafka是如何简化运维操作的。Kafka使用多副本来保证消息不丢失。Multi-copy涉及到Kafka的复制机制。在一个超大规模的集群中,时不时会出现这个点的磁盘坏掉,那个点的CPU负载高,导致各种问题。,多副本之间的复制,如果要完全自动化容错,就得做一些考虑和取舍。下面举个例子来说明运维中所面临的复杂性。我们都知道Kafka有一个ISR集。先解释一下这个概念:Kafka不是完全同步的,也不是完全异步的。它是一种ISR机制:1.leader它会维护一个Replicas列表,这些Replicas基本上和它保持同步。此列表称为ISR(同步副本)。每个Partition都会有一个ISR,由leader动态维护。2.如果一个follower落后于leader过多,或者一段时间内没有发起数据复制请求,leader会将其从ISR中移除。3、当ISR中的所有replica都向leader发送ACK后,leader才会commit。只有这样,生产者才能认为请求中的所有消息都已提交。在这种机制下,如果一个producer在一个request中发送的消息过多,一瞬间花落后leader怎么办?如果follower不停地进出ISR,会不会影响性能?如果这种情况加上Alarming,可能会造成警报轰炸。如果我们不加告警,如果因为IO性能或者GC问题导致broker挂掉或者broker落后于leader,真的需要告警怎么办?今天我们就来看看Kafka的设计是如何让我们在运维中彻底避免这种头疼的问题。2.Kafka的复制机制Kafka的每个分区都是由不可变的消息序列依次追加组成,每条消息都有一个唯一的偏移量来标记位置。Kafka中的复制机制是按分区粒度进行复制的。当我们在Kafka中创建主题时,我们可以设置一个复制因子。这个复制因子决定了分区副本的数量。如果leader挂了,Kafka会把这个partition的master节点failover到其他replica节点,这样就可以保证这个partition的消息可用。领导节点负责接收来自生产者的消息,其他副本节点(跟随者)从主节点复制消息。kakfa日志复制算法提供的保证是,一条消息在producer端被认为是committed后,如果leader节点挂了,其他节点被选举为leader节点,这条消息也可以被消费。这种情况下,leader选举时,只能从ISR集合中选出。集合中的每个点都必须与领导者消息同步,即没有延迟。分区的领导者维护ISR集列表。如果一个点落后太多,就会被踢出ISR集合。生产者向领导节点发送消息后,只有当ISR中的所有副本都向领导发送ACK以确认消息时,领导才会提交。这时候生产者就可以考虑提交消息了??。正因为如此,kafka客户端的写入性能取决于从ISR集合中最慢的broker接收消息的性能。如果一个节点性能不佳,必须尽快识别它,然后将其踢出ISR集,以避免性能问题。3、replica如何才能算是跟得上leader的replica?如果副本无法“赶上”领导节点,它可能会被踢出ISR集。下面举个例子来说明什么是真正的“赶上来”——与leader节点消息同步。Kafka中的一个单分区topic——foo,复制因子为3,分区分布和leader、follower如下图所示,现在broker2和3都是follower,都在ISR集合中。我们设置replica.lag.max.messages为4,只要follower不落后于leader超过3条消息,就是一个能跟上leader的节点,不会被踢出。设置replica.lag.time.max.ms为500ms,也就是说follower只要每隔500ms发送一次fetch请求,就不会被认为死了,不会被踢出ISR集合。现在producer发送了一条offset为3的消息,此时broker3发生了GC,如下图:因为broker3现在在ISRset中,任意一个broker3拉取并同步了这条offset消息of3,or3被删除从ISR集合中踢出,否则这条消息不会被提交,因为replica.lag.max.messages=4为4,broker3只落后一条消息,不会被踢出ISR集合,broker3如果此时GC100ms,GC结束,然后拉取offset为3的消息,再次与leader完全同步。整个过程一直在ISR集中,如下图所示:4.一个副本什么时候会被踢出ISR集?一个replica被踢出ISR集有几个原因:一个replica在一段时间内跟不上leader节点,即与leader节点的差距大于replica.lag.max.messages。通常,IO性能跟不上。或者CPU负载太高以至于代理将消息附加到磁盘的速度比接收领导消息的速度慢。一个broker长时间没有向leader发送fetch请求(大于replica.lag.time.max.ms),可能是因为broker发生了fullGC,或者其他原因挂掉了。新的代理节点,例如相同的代理ID、磁盘故障、新机器或重新分配给新代理节点的分区,将从分区领导者上最旧的现有消息同步。因此,在Kafka0.8版本之后,设置了两个参数,replica.lag.max.messages用于识别性能已经变慢的节点,replica.lag.time.max.ms用于识别卡住的节点。5、什么情况下一个节点真正落后从上面的情况来看,两个参数似乎就够用了。如果一个副本超过了replica.lag.time.max.ms,还没有发送fetch同步请求,可以认为这个replica节点卡住了,被踢出,但是还有一种特殊情况没有考虑。我们在上面将replica.lag.max.messages设置为4。之所以设置为4,是因为我们已经知道生产者每次请求发送的消息数小于4。如果我们的参数应用于多个主题,那么这个生产者最大发送的消息数为不好估计,或者在流量频繁抖动的情况下,会发生什么?举个例子来说明:如果我们的topic——foo的producer因为流量抖动调用了一个包含4条消息的request,我们设置replica.lag.max.messages为4,此时所有的follower都会被踢出ISR集合因为超过了backwardmessages的数量:那么,因为follower是正常的,所以下一次fetch请求会再次追上leader。这时,它会再次加入ISR集合。如果它频繁抖动,它会不断地进出ISR集,这会导致头痛和警报轰炸。这里的核心问题是,在topic数量较多或者流量抖动频繁的情况下,我们无法对topic生产者每次发送的消息数量做任何假设,因此很难确定一个合适的epilica.lag.max.messagesvalue6.一个配置就全部搞定了。其实不正常的只有两种情况,一种是卡死了,另一种是follower的性能变慢了。如果我们仅仅根据follower落后于leader多少来判断是否应该设置follower来提出一个ISR集合,就需要对flow进行预测和估计。我们怎样才能避免这种不可靠的估计呢?kafka给出的解决方案如下:replica.lag.time.max.ms的配置意义做了增强,和之前一样,如果follower卡住时间超过这个时间,没有发送fetch请求,它将被踢出ISR集。新的增强逻辑是,当follower落后leader超过eplica.lag.max.messages条消息时,不会立即踢出ISR集,而是继续落后超过replica就会被踢出.lag.time.max.ms,以免流量抖动带来运维问题,因为follower会跟着下一次fetch到leader,所以不需要对写入速度做任何预估话题。
