Kafka概览

Kafka

Kafka 是一种高吞吐量、分布式、基于发布/订阅的消息系统,最初由 LinkedIn 公司开发,使用Scala 语言编写,目前是 Apache 的开源项目。

Kafka 中的概念及其含义:

  1. broker: Kafka 服务器,负责消息存储和转发
  2. topic:消息类别, Kafka 按照 topic 来分类消息
  3. partition: topic 的分区,一个 topic 可以包含多个 partition, topic 消息保存在各个partition 上
  4. offset:消息在日志中的位置,可以理解是消息在 partition 上的偏移量,也是代表该消息的唯一序号
  5. Producer:消息生产者
  6. Consumer:消息消费者
  7. Consumer Group:消费者分组,每个 Consumer 必须属于一个 group
  8. Zookeeper:保存着集群 broker、 topic、 partition 等 meta 数据;另外,还负责 broker 故障发现, partition leader 选举,负载均衡等功能

Kafka架构图

Kafka 数据存储设计

Partition

对应于其他消息中间件中的队列。Partition 中每条 Message 包含offsetMessageSizedata

offset 表示 Message 在这个 partition 中的偏移量, offset 不是该 Message 在 Partition 数据文件中的实际存储位置,而是逻辑上一个值,它唯一确定了 Partition 中的一条 Message,可以认为 offset 是partition 中 Message 的 id。 MessageSize 表示消息内容 data 的大小。 data 为 Message 的具体内容。

Segment

Partition 物理上由多个 Segment 文件组成,每个 Segment 大小相等,顺序读写。每个 Segment 数据文件以该段中最小的 offset 命名,文件扩展名为.log。这样在查找指定 offset 的 Message 的时候,用二分查找就可以定位到该 Message 在哪个Segment 数据文件中。

索引

Kafka 为每个分段后的数据文件(Segment文件)建立了索引文件,文件名与数据文件的名字是一样的,只是文件扩展名为.index。 index 文件中并没有为数据文件中的每条 Message 建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。

索引文件示意图

Producer 设计

负载均衡

由于消息 topic 由多个 partition 组成, 且 partition 会均衡分布到不同 broker 上,因此,为了有效利用 broker 集群的性能,提高消息的吞吐量, producer 可以通过随机或者 hash 等方式,将消息平均发送到多个 partition 上,以实现负载均衡。

批量发送

批量发送是提高消息吞吐量重要的方式, producer 端可以在内存中合并多条消息后, 以一次请求的方式发送了批量的消息给 broker,从而大大减少 broker 存储消息的 IO 操作次数。但也一定程度上影响了消息的实时性,相当于以时延为代价,换取更好的吞吐量。

压缩

Producer 端可以通过 GZIPSnappy 格式对消息集合进行压缩。 Producer 端进行压缩之后,在Consumer 端需进行解压。压缩的好处就是减少传输的数据量,减轻对网络传输的压力,在对大数据处理上,瓶颈往往体现在网络上而不是 CPU(压缩和解压会耗掉部分 CPU 资源)。

Consumer 设计

Comsumer Group

同一 Consumer Group 中的多个 Consumer 实例,不同时消费同一个 partition,等效于队列模式,当某个实例挂掉的时候,其他实例会自动地承担起它负责消费的 partition。 partition 内消息是有序的, Consumer 通过 pull 方式消费消息。 Kafka 不删除已消费的消息对于 partition,顺序读写磁盘数据,以时间复杂度 O(1)方式提供消息持久化能力。

ZooKeeper 在 Kafka 中的作用

  • Broker 注册 :在 Zookeeper 上会有一个专门用来记录 Broker 服务器列表的节点。每个 Broker 在启动时,都会到 Zookeeper 上进行注册,即到/brokers/ids 下创建属于自己的节点。每个 Broker 就会将自己的 IP 地址、端口、JMS 端口等信息记录到该节点里。示例如下:
{"listener_security_protocol_map":{"PLAINTEXT":"PLAINTEXT"},"endpoints":["PLAINTEXT://192.168.30.21:9092"],"jmx_port":-1,"port":9092,"host":"192.168.30.21","version":4,"timestamp":"1610004322517"}
  • Topic 注册 : 在 Kafka 中,同一个Topic 的消息会被分成多个分区并将其分布在多个 Broker 上,这些分区信息及与 Broker 的对应关系也都是由 Zookeeper 在维护。比如我创建了一个名字为 hello-topic 的主题并且它有两个分区,对应到 zookeeper 中会创建这些结点:/brokers/topics/hello-topic/Partitions/0/brokers/topics/hello-topic/Partitions/1
  • 负载均衡 :Kafka 通过给特定 Topic 指定多个 Partition,而各个 Partition 可以分布在不同的 Broker 上,这样便能提供比较好的并发能力。 对于同一个 Topic 的不同 Partition,Kafka 会尽力将这些 Partition 分布到不同的 Broker 服务器上。当生产者产生消息后也会尽量投递到不同 Broker 的 Partition 里面。当 Consumer 消费的时候,Zookeeper 可以根据当前的 Partition 数量以及 Consumer 数量来实现动态负载均衡。

几个重要问题

Kafka 如何保证消息的顺序性

在实际业务场景中,有时我们需要保证消息的顺序性以达到正确的逻辑,Kafka 中主要有一下几种方式来保证消息的顺序性。

  • 一个 Topic 只包含一个 Partition 。

    由于同一个 Partition 中的消息是有序的(offset),所以如果一个 Topic 只包含一个 Partition 就可以保证顺序性,但是不推荐,因为这样会产生很多问题,如:没有备份,无法容灾;不能实现负载均衡等。

  • 生产消息时指定 Key 或 Partition 。

    在生产消息时如果我们指定 Key ,那么 Kafka 会对 Key 进行哈希,进而决定消息发送到 Topic 下的哪个 Partition 上。或者也可以直接指定 Partition 。它们都是利用同一个 Partition 上的消息是顺序的特点。

Kafka 如何保证消息不丢失

生产者消息丢失

Kafka 支持生产者发送消息失败时可以进行重试,主要是通过生产者配置项retries指定重试次数。

消费者消息丢失

消息在被发送到 Partition 的时候都会分配一个特定的 offset。offset 表示 Consumer 当前消费到的 Partition 的所在的位置。Kafka 通过offset 保证消息在分区内的顺序性。

当消费者拉取到了 Partition 的某个消息之后,默认情况下消费者会自动提交 offset 。当消费者刚拉取到消息还没开始消费时,由于默认自动提交 offset,那么 Kafka 会认为已经成功消费,而这时消费者挂了,就会导致这条消息没有被消费,也就是丢失了。解决方法是将自动提交 offset 设置为 false 。消费完后手动提交。但又会带来新的问题:当消费者已经消费完了某条消息,还没有来得及手动提交 offset 时挂了,那么 Kafka 就会认为这条消息还没有被消费,于是下次还是会从这条消息开始消费,导致重复消费了这条消息。解决方法在下一个部分给出。

Kafka 消息丢失

从前面的架构图可以看出,Kafka 的消息保存在 Partition 上,并且为 Partition 引入了多复本(Replica)机制。也就是说每个 Partition 都有复本,均匀分布在多个 Broker 上,多个复本中有一个 Leader,其他都是 Follower。我们发送的消息会被发送到 Leader 副本,然后 Follower 副本才能从 Leader 副本中拉取消息进行同步。生产者和消费者只与 Leader 副本交互。 Follower 复本是为了保证数据的安全性。

假如 Leader 副本所在的 Broker 突然挂掉,那么就要从 Follower 副本重新选出一个 Leader ,但是 Leader 的数据还有一些没有被 Follower 副本的同步的话,那一部分消息就会丢失。可以通过配置进行解决:

  1. 配置acks = all。应答级别。
    acks=0 代表把消息发送到kafka就认为发送成功
    acks=1 代表把消息发送到kafka leader分区,并且写入磁盘就认为发送成功
    acks=all 代表把消息发送到kafka leader分区,并且 Leader 分区的副本 Follower 对消息进行了同步就认为发送成功

  2. 配置replication.factor >= 3。为了数据的安全性,我们一般会为 topic 设置 replication.factor >= 3。这样就可以保证每个 Partition 至少有 3 个副本。

  3. 配置min.insync.replicas > 1。这样配置代表消息至少要被写入到 2 个副本才算是被成功发送。一般推荐设置成 replication.factor = min.insync.replicas + 1

  4. 配置unclean.leader.election.enable = false。这样配置代表当 Leader 副本发生故障时就不会从 Follower 副本中和 Leader 同步程度达不到要求的副本中选择出 Leader ,这样降低了消息丢失的可能性。

Kafka 如何保证消息不重复消费

重复消费的本质就是消费者消费了消息,但是提交 offset 失败了,导致 Kafka 不知道消费者已经消费那些消息,消费者下次重启导致再次消费这些消息。

解决方案是:给消息添加一个全局唯一ID,每次消费消息时到 Redis/MongoDB 等外部中间件里查询是否消费过。或者利用数据库主键冲突。

我的更多文章尽在:我的个人博客

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值