浅谈Kafka设计

  1. Kafka 将消息以 topic 为单位进行归纳
  2. 将向 Kafka topic 发布消息的程序称为 producers
  3. 将预订 topics 并消费消息的程序成为 consumer
  4. Kafka 以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个 broker.
  5. producers 通过网络将消息发送到 Kafka 集群,集群向消费者提供消息

Kafka如何保证高可用

Kafka 的基本架构组成是:由多个 broker 组成一个集群,每个 broker 是一个节点;当创建一个topic 时,这个 topic 会被划分为多个 partition ,每个 partition 可以存在于不同的 broker上,每个 partition 只存放一部分数据。

这就是天然的分布式消息队列,就是说一个 topic 的数据,是分散放在多个机器上的,每个机器就放一部分数据

在 Kafka 0.8 版本之前,是没有 HA 机制的,当任何一个 broker 所在节点宕机了,这个 broker 上 的 partition 就无法提供读写服务,所以这个版本之前, Kafka 没有什么高可用性可言

在 Kafka 0.8 以后,提供了 HA 机制,就是 replica 副本机制。每个 partition 上的数据都会同步到其它机器,形成自己的多个 replica 副本。所有 replica 会选举一个 leader 出来,消息的生产者和消费者都跟这个 leader 打交道,其他 replica 作为 follower 。写的时候, leader 会负责把数据同步到所有 follower 上去,读的时候就直接读 leader 上的数据即可。 Kafka 负责均匀的将一个 partition 的所有 replica 分布在不同的机器上,这样才可以提高容错性。

image-20210921120525433

拥有了 replica 副本机制,如果某个 broker 宕机了,这个 broker 上的 partition 在其他机器上还存在副本。如果这个宕机的 broker 上面有某个 partition 的 leader ,那么此时会从其follower 中重新选举一个新的 leader 出来,这个新的 leader 会继续提供读写服务,这就有达到了所谓的高可用性

写数据的时候,生产者只将数据写入 leader 节点, leader 会将数据写入本地磁盘,接着其他follower 会主动从 leader 来拉取数据, follower 同步好数据了,就会发送 ack 给 leader , leader 收到所有 follower 的 ack 之后,就会返回写成功的消息给生产者。消费数据的时候,消费者只会从 leader 节点去读取消息,但是只有当一个消息已经被所有 follower都同步成功返回 ack 的时候,这个消息才会被消费者读到

image-20210921120651307

Kafka 消息是采用 Pull 模式,还是 Push 模式

生产者使用push模式将消息发布到Broker,消费者使用pull模式从Broker订阅消息。

push模式很难适应消费速率不同的消费者,如果push的速度太快,容易造成消费者拒绝服务或网络拥塞;如果push的速度太慢,容易造成消费者性能浪费。但是采用pull的方式也有一个缺点,就是当Broker没有消息时,消费者会陷入不断地轮询中,为了避免这点,kafka有个参数可以让消费者阻塞知道是否有新消息到达

什么是消费者组

消费者组是Kafka独有的概念,即消费者组是Kafka提供的可扩展且具有容错性的消费者机制。

但实际上,消费者组(Consumer Group)其实包含两个概念,作为队列,消费者组允许你分割数据处理到一组进程集合上(即一个消费者组中可以包含多个消费者进程,他们共同消费该topic的数据),这有助于你的消费能力的动态调整;作为发布-订阅模型(publish-subscribe),Kafka允许你将同一份消息广播到多个消费者组里,以此来丰富多种数据使用场景。需要注意的是:在消费者组中,多个实例共同订阅若干个主题,实现共同消费。同一个组下的每个实例都配置有相同的组ID,被分配不同的订阅分区。当某个实例挂掉的时候,其他实例会自动地承担起它负责消费的分区。 因此,消费者组在一定程度上也保证了消费者程序的高可用性。

Kafka中,ZooKeeper的作用是什么

目前,Kafka使用ZooKeeper存放集群元数据、成员管理、Controller选举,以及其他一些管理类任务。之后,等KIP-500提案完成后,Kafka将完全不再依赖于ZooKeeper

  • “存放元数据”是指主题分区的所有数据都保存在 ZooKeeper 中,且以它保存的数据为权威,其他

    “人” 都要与它保持对齐。

  • “成员管理” 是指 Broker 节点的注册、注销以及属性变更,等等

  • “Controller 选举” 是指选举集群 Controller,而其他管理类任务包括但不限于主题删除、参数配置

    等。

Kafka中位移(offset)的作用

在Kafka中,每个主题分区下的每条消息都被赋予了一个唯一的ID数值,用于标识它在分区中的位置。这个ID数值,就被称为位移,或者叫偏移量。一旦消息被写入到分区日志,它的位移值将不能被修改。

kafka producer发送数据,ack01-1分别是什么意思

  • 1 (默认) 数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了。在这种

    情况下,如果leader宕机了,则会丢失数据。

  • 0生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据

    可靠性确是最低的。

  • -1 producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。

    当ISR中所有Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请

    求中的消息都commit了。

Kafka如何保证消息不丢失

消息丢失的原因

对于一个消息队列,会有 生产者 、 MQ 、 消费者 这三个角色,在这三个角色数据处理和传输过程中,都有可能会出现消息丢失

消费者异常导致的消息丢失

消费者可能导致数据丢失的情况是:消费者获取到了这条消息后,还未处理, Kafka 就自动提交了offset ,这时 Kafka 就认为消费者已经处理完这条消息,其实消费者才刚准备处理这条消息,这时如果消费者宕机,那这条消息就丢失了。消费者引起消息丢失的主要原因就是消息还未处理完 Kafka 会自动提交了 offset ,那么只要关闭自动提交 offset ,消费者在处理完之后手动提交 offset ,就可以保证消息不会丢失。但是此时需要注意重复消费问题,比如消费者刚处理完,还没提交 offset ,这时自己宕机了,此时这条消息肯定会被重复消费一次,这就需要消费者根据实际情况保证幂等性。

生产者数据传输导致的消息丢失

对于生产者数据传输导致的数据丢失主常见情况是生产者发送消息给 Kafka ,由于网络等原因导致消息丢失,对于这种情况也是通过在 producer 端设置 acks=all 来处理,这个参数是要求 leader 接收到消息后,需要等到所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试。

broker导致的消息丢失

Kafka 导致的数据丢失一个常见的场景就是 Kafka 某个 broker 宕机,,而这个节点正好是某个partition 的 leader 节点,这时需要重新重新选举该 partition 的 leader 。如果该 partition 的 leader 在宕机时刚好还有些数据没有同步到 follower ,此时 leader 挂了,在选举某个follower 成 leader 之后,就会丢失一部分数据。

Kafka 可以设置如下 4 个参数,来尽量避免消息丢失:

  • 给 topic 设置 replication.factor 参数:这个值必须大于 1 ,要求每个 partition 必须有

    至少 2 个副本

  • 在 Kafka 服务端设置 min.insync.replicas 参数:这个值必须大于 1 ,这个参数的含义是一个leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader挂了还有一个 follower 节点

  • 在 producer 端设置 acks=all ,这个是要求每条数据,必须是写入所有 replica 之后,才能认

    为是写成功了;

  • 在 producer 端设置 retries=MAX (很大很大很大的一个值,无限次重试的意思):这个参数的

    含义是一旦写入失败,就无限重试,卡在这里了。

Kafka 如何保证消息的顺序性

在某些业务场景下,我们需要保证对于有逻辑关联的多条MQ消息被按顺序处理,比如对于某一条数据,正常处理顺序是 新增-更新-删除 ,最终结果是数据被删除;如果消息没有按序消费,处理顺序可能是 删除-新增-更新 ,最终数据没有被删掉,可能会产生一些逻辑错误。对于如何保证消息的顺序性,主要需要考虑如下两点:

  • 如何保证消息在 Kafka 中顺序性
  • 如何保证消费者处理消费的顺序性

如何保证消息在 Kafka 中顺序性

对于 Kafka ,如果我们创建了一个 topic ,默认有三个 partition 。生产者在写数据的时候,可以指定一个 key ,比如在订单 topic 中我们可以指定订单 id 作为 key ,那么相同订单 id 的数据,一定会被分发到同一个 partition 中去,而且这个 partition 中的数据一定是有顺序的。消费者从partition 中取出来数据的时候,也一定是有顺序的。通过制定 key 的方式首先可以保证在 kafka内部消息是有序的。

如何保证消费者处理消费的顺序性

对于某个 topic 的一个 partition ,只能被同组内部的一个 consumer 消费,如果这个 consumer内部还是单线程处理,那么其实只要保证消息在 MQ 内部是有顺序的就可以保证消费也是有顺序的。但是单线程吞吐量太低,在处理大量 MQ 消息时,我们一般会开启多线程消费机制,那么如何保证消息在多个线程之间是被顺序处理的呢?对于多线程消费我们可以预先设置 N 个内存 Queue ,具有相同 key的数据都放到同一个内存 Queue 中;然后开启 N 个线程,每个线程分别消费一个内存 Queue 的数据即可,这样就能保证顺序性。当然,消息放到内存 Queue 中,有可能还未被处理, consumer 发生宕机,内存 Queue 中的数据会全部丢失,这就转变为上面提到的如何保证消息的可靠传输的问题了

Kafka的哪些场景中使用了零拷贝

在Kafka中,体现Zero Copy使用场景的地方有两处:基于mmap的索引和日志文件读写所用的TransportLayer。先说第一个。索引都是基于MappedByteBuffer的,也就是让用户态和内核态共享内核态的数据缓冲区,此时,数据不需要复制到用户态空间。不过,mmap虽然避免了不必要的拷贝,但不一定就能保证很高的性能。在不同的操作系统下,mmap的创建和销毁成本可能是不一样的。很高的创建和销毁开销会抵消Zero Copy带来的性能优势。由于这种不确定性,在Kafka中,只有索引应用了mmap,最核心的日志并未使用mmap机制。再说第二个。TransportLayer是Kafka传输层的接口。它的某个实现类使用了FileChannel的transferTo方法。该方法底层使用sendfile实现了Zero Copy。对Kafka而言,如果I/O通道使用普通的PLAINTEXT,那么,Kafka就可以利用Zero Copy特性,直接将页缓存中的数据发送到网卡的Buffer中,避免中间的多次拷贝。相反,如果I/O通道启用了SSL,那么,Kafka便无法利用Zero Copy特性了。