Kafka
Apache Kafka is an open-source distributed event streaming platform used by thousands of companies for high-performance data pipelines, streaming analytics, data integration, and mission-critical applications.
一、Kafka 基本概念
1、事件
事件是向 kafka 中读取或写入数据时的最小单位,由 key、value、timestamp 以及元数据表头组成。
事件流是按时间排序的一系列业务事件
2、Topic
kafka 提出了 topic 这个概念,topic 可以理解为事件流的持久化,或者说当一个事件流进入 kafka 之后,它就成为了一个 topic 主题
一个 topic 可以有多个生产者以及多个消费者,生产者向 topic 中推送消息,消费者从 topic 中拉取消息。
3、Partition
Kafka 中的一切都是围绕分区建立的,它们控制着 Kafka 的存储、可伸缩性、复制和消息流动。
1)概述
一个 topic 可以被分为多个 partition,但要注意的是 topic 是一个逻辑概念,partiton 才是物理层面的最小存储单元,其中组成一个 topic 的多个 partition 可能会分布在多个 kafka broker 上(一个 kafka 服务器就是一个 broker)
2)原理
当一个事件发布到一个 topic 时,实际上是添加到对应 partition 的队尾,而且相同的事件 key 会添加到相同的 parttition 中。
partition 本质是一个 log 文件,每条记录都是以追加的形式写入。
kafka 还会为 partition 内部的每条记录维护一个唯一序列号,称为 offset,当一条记录写入时,就会被分配一个序号 offset。
但是 offset 只在每个 partiton 内部维护,也就是说消息的有序性只能在一个 partition 内部保证,而在 topic 层面是无法保证的(一个 topic 内只有一个 partition 时情况除外)。
3)扩展性
如果一个 topic 的所有 partition 都在同一个 broker 中,那么该 topic 的性能就会受这个 broker 的 IO 吞吐量的限制。
但如果我们把一个 topic 的 partition 分布到多个 broker 上面,那此时 topic 的性能,将远超部署在单个 broker 的 topic 的性能,这就是 partition 所带给我们的水平扩展性。
一个 topic 可以被多个 consumer 或者同个 consumer 的多个实例并行消费,如果 topic 的 partition 都在同个 broker,那么支持的 consumer 数量有限并且给服务器的负载很大,所以分散到多个 broker 可以很大程度提高消息处理能力,以及降低单个服务器的负载。
4)备份
Kafka 为一个 Partition 生成多个副本,并且把它们分散在不同的 Broker。多副本的机制大程度提高了服务的容灾能力。
分区的副本之间是一主多从的关系,其中 leader 负责处理读写请求,follower 负责和 leader 同步消息,如果 leader 出现故障,就会从 follower 中选举新的 leader,保证了服务的高可用。
其中副本因子表示一个分区的副本数,比如副本因子为 3 就表示 1 个 leader 副本和 2 个 follower 副本。
我们把分区的所有副本称为 AR,与 leader 保持一定程度同步的副本称为 ISR(包括 leader 副本),与 leader 滞后过多的副本称为 OSR,显然 AR = ISR + OSR。其中“一定程度同步”可以通过参数设置。
我们会从 ISR 中选举新的 leader,并且将 ISR 中落后的副本剔除,将 OSR 奋进的副本加入 ISR 中。
5)partition 的写入
当我们作为 producer 向 kafka 发布消息时,如何知道消息进入了哪个 partition 呢?
- 方式一:指定 partition key,也就是前面提到的事件 key。这样可以保证有序,但会造成热点 partition 问题。
- 方式二:kafka 轮询,这样可以做到均衡分布,但是无法保证有序。
- 方式三:自定义规则。
6)partition 的读取
kafka 的消费者需要自己去 partition 中拉取消息,并且读取时的 offset 由消费者自己进行维护。 消费者自己维护 offset 有很多好处,比如减少 kafka 负担、避免重复消费、断电宕机后可恢复等。
怎么区分 partition 属于哪个 topic ?
4、消费者组
特性:一个分区在消费组间共享,消费组中互斥。
消费模式灵活:
分区广播模式:一个组中只有一个消费者,那么分区就可以广播所有消费者
分区单播模式:一个组内多个消费者,那么分区只能选择一个消费者单播
二、Kafka 应用实战
1、go 客户端操作 kafka
2、怎么设置合适的分区数目?
我们在创建 topic 的时候,需要指定 partition 的数量,经过以上的分享我们知道分区是 kafka 最小的并行操作单元,对于生产者而言数据的写入是可以并行化的,对于消费者组而言,它的消费并行度也完全依赖于所消费 topic 的分区数量。如此看来,如果一个主题中的分区数越多,理论上所能达到的吞吐量就越大,但事实真的同预想的一致嘛?
[图片]
通过上图可以发现,分区数的增加会带来性能上的提升,但是一旦分区数超过了某个阈值之后,整体的吞吐是不升反降的。具体的原因可能是以下几点:
- 分区本质是文件会占用文件描述符,而一个进程的所能支配的文件描述符有限,所以过多的分区会造成不必要的资源开销。
- 分区数过多会使系统的整体可用性降低,我们知道每个分区都有一至多个副本,并分布在不同的 broker 上面。那如果分区数很多,一个 broker 上就可能存在很多 leader 副本。如果此时 broker 宕机,就会触发多个分区的 leader 选举机制,此时被选举分区都会处于不可用状态。因为发生选举的分区数量过多,导致完成所有分区选举的时间过长,从而使服务整体的可用性降低。
3、创建主题后还可以增加分区数吗?
其实是可以的,kafka 允许我们在主题创建后对其做一定的修改。但我们需要注意些一问题。
我们需要严谨对待基于 key 计算的主题,我们知道当生产者向 kafka 中写入基于 key 的消息时,kafka 会通过消息的 key 来计算出消息将要写入具体哪个分区,也就是相同 key 的消息会写入同一个分区。
但其实 kafka 在拿到 key 之后会进行哈希(MurmurHash2 算法)来计算分区号,所以当 kafka 工作一段时间后,我们再去增加一些分区,这可能会间接影响哈希算法,从而导致相同的 key 与之前计算出的哈希值不同,这对于那些对有序性有要求的应用来说是致命的。
所以我们建议在创建主题时,最好能确定好分区数,尤其对于与 key 高关联的应用,在创建主题时可以适当地多创建一些分区,以满足未来的需求。
4、消息的可靠性保证
我们可以通过调节 kafka 客户端的 acks 参数来对消息的可靠性做出一定保障。
acks = -1:生产者将消息发送到 leader 副本,leader 副本在成功写入本地日志之后还要等待 ISR 中的 follower 副本全部同步完成才能够告知生产者已经成功提交。
acks = 1:生产者将消息发送到 leader 副本,leader 副本在成功写入本地日志之后就会告知生产者已经成功提交,此时 leader 副本所在 broker 宕机,消息就不会被同步到 follower 副本从而导致消息丢失。
acks = 0:生产者将消息发送之后,不会等 leader 副本确认,直接发送下一条消息。
我们也可以通过改变 offset 提交机制来保证消息的可靠性。
当 enable.auto.commit 参数的默认值为 true,会开启自动位移提交的功能,但此时会带来重复消费和消息丢失的问题。对于高可靠要求的应用来说,宁愿重复消费也不应该因为消费异常而导致消息丢失。所以可以将 enable.auto.commit 参数设置为 false 来执行手动位移提交。在执行手动位移提交的时候也要遵循一个原则:如果消息没有被成功消费,那么就不能提交所对应的消费位移。