写在前面

  • 如果博客中有什么说的不对的,还请大家指出,感谢。

一,分区策略

这里为什么会说到分区策略的问题呢,因为在kafka中有主题(topic)的概念,在topic中就有分区的策略,分区的原因就是为了减轻kafka服务器的压力,提高并发量。将一个主题分成几个分区,然后生产者发送消息的时候,就将消息发送到某一个分区中。这也是为啥讨论生产者的时候需要提到主题的分区策略问题。

1,生产者往kafka发送消息的方式

一般我们通过代码的方式往kafka中发送消息的时候,有三种发送的方式:

  • 第一种
    指明partition的情况,直接向对应的分区进行发送消息的操作。
  • 第二种
    没有指明partition的情乱,但是代码中传入了key值,那么此时就将key的hash值与分区的数量进行取模运算,得到的结果作为partition的值。
  • 第三种
    代码中既没有传入partition,又没有key值得情况,那么此时第一次调用就会随机产生一个整数,以后再次调用得时候,在这个整数上做自增运算,然后这个值与分区数进行取模运算,得到的结果作为partition得值。
2,kafka中副本之间数据的一致性

在kafka中,我们创建主题的时候,一般需要设置分区数和副本的数量,对于副本中,会选取一个leader节点去接受生产者发送来的消息,为了保证follower节点的数据和leader节点的数据保持一致,leader节点去将接受到的消息发送到follower节点。并且在生产者发送消息到kafka中的时候,有一种类似于tcp中的ack机制,那么此时就有一个问题,就是当生产者往kafka中的leader节点中发送消息的时候,leader节点需要等到什么身后去给生产者发送ack的消息呢?

实际上在kafka中,leader节点是将生产者发送的消息发送到所有的follower节点之后,才会往生产者发送一个ack的消息,目的是为了保证follower节点和leader节点的数据一致性。

生产者发送消息的大概流程:

kafka 节点连接失败_发送消息

3,ISR的提出

上面谈论到的kafka保证副本之间数据的一致性问题的方法中,其实是含有一个问题的,假如在某一个主题的副本中,当leader节点与该follower节点同步数据的时候,follower节点如果传输的速率比较的低,那么此时leader节点就要一直的等待当前该follower节点,这种方式的效率明显是比较的低的,此时就提出了ISR的概念,ISR是一个副本组成的集合,也就是说,leader节点要与当前分区中的所有follower节点进行数据同步的时候,并不是与所有的follower节点都进行同步操作,而是与ISR中的follower节点进行同步数据的操作。此时又会有一个问题,就是说什么样的follower节点会加入到ISR的集合当中呢,在kafka中,有一个replica.lag.time.max.ms这个参数,它是表示当follower节点与leader节点同步的时间超过了该值,那么就将该follow节点移除ISR的集合中。

4,ack的应答机制

在ack应答的机制当中,kafka为用户提供了三种可靠性级别,这三种的可靠级别通过acks这个参数进行配置。

  • 第一种
    acks=0的时候,生产者不去等待leader节点发来的ack信息,只管发数据。不管数据有没有发送成功。这种方式会有很大的可能造成数据的丢失,因为生产者不会去管自己传输的数据有没有成功,没有重传的机制。所以对于那些没有传输成功的消息,就会出现丢失的情况。
  • 第二种
    acks=1的时候,leader节点接收到生产者发送来的消息之后,就往生产者发送一个ack的消息,这种方式相比于第一种方式数据的丢失性要降低,因为它保证了leader节点中的数据是完整的,但是这种情况却不能保证follower节点和leader节点中的数据的一致性。比如说:当leader节点接收完生产者发送的消息之后,leader节点发生了故障挂掉了,并且此时还没有同步到follower节点的数据,此时由于生产者不会重新发送之前的消息,此时重新从follower节点中选举的leader节点就会造成数据的丢失的情况。
  • 第三种
    acks=-1的情况,这种情况是当leader接受完消息,并且将数据同步到ISR中的其他节点之后,才会返回ack的消息,这种方式看起来没什么问题,但是也存在一种情况就是会出现数据重复的问题,就是说生产者可能会发送两次相同的消息。例如:当leader和follower节点同步完成之后,此时leader节点挂了,那么此时会重新选举新的leader节点,但是新的leader节点中含有刚才生产者发送的消息,但是由于生产者没有接收到ack的消息,所以生产者会重新发送刚才的消息,这样会导致一个消息生产者发送了两次的情况。

    那在acks=-1的基础之上怎么解决数据的重复性呢,在kafka的0.11版本中,引入了幂等性的概念,也就是说在一次的会话当中,同一个生产者发送多次相同的信息,Server端只会持久化一次。这种方式即为Exactly One语义。它是通过给生产者分配一个PID(Producer ID),然后在生产者发送消息的同时,在消息上会附带着SeqNumber的信息用来标记信息,然后Broker端就会对<PID,Partition,SeqNumber>做一次缓存处理,当具有相同主键信息的消息提交的时候,Broker只会持久化一条。注意:这种方式只能保证在一次会话中,并且还不能跨分区。也就是说幂等性是不能保证跨分区,跨会话的Exactly Once。
5,kafka节点发生故障时的处理(存储数据的一致性)

kafka集群中的leader节点出现问题的时候,follower和leader的消息的偏移量不一定是一样的,此时如果leader出现挂机的状况,就会出现很多的问题。
概念

  • LEO(log End Offset):指的是每个副本的最大offset
  • HW(High Watermark):某一分区中的所有副本的最小LEO

两者的概念如下图所示:

kafka 节点连接失败_kafka 节点连接失败_02


分析

出故障的话分为两种情况

  • leader节点发生故障
    当是这种情况的时候,会从ISR的集合中选取一个新的leader节点,并且计算出HW的值,并且截取每个log文件中高于HW值得部分,目的是为了保证消费者消费得数据一定存在,即消费的一致性。
  • follower节点发生故障
    当follower节点发生故障的时候,会将故障的follower节点踢出ISR的集合中,等到follower节点恢复之后,该节点会从磁盘中读取上次的HW,然后截取自身log文件中高于HW的部分,并且从HW开始同步,知道与leader节点同步完成,最终重新加入ISR的集合当中。

这两种情况的分析只能保证数据存储的一致性,但是不能保证数据的丢失和数据重复等情况,有关数据的丢失或者重复性问题应该又asks参数决定。