Kafka 设计基础原理

背景介绍

对于一个被广泛使用的分布式消息系统,大名鼎鼎的kafka有诸多优点,之前仅是用kafka的单机版,最近因为项目的需要,需要调研kafka集群,了解关于kafka的风险,最后结合之前所学的,在这里和大家一起分享。

前提:一个优秀的消息系统应该具备什么?

高吞吐 即使在一台廉价的机器上,也可以达到尽可能多的每秒写入量,是一个合格的消息系所必需的。
数据的可靠性 数据能够准确被送达至消息系统,并可以正确被从消息系统中取出, 在这个过程中不会出现乱序、丢失
扩展性 可以随时扩展消息系统的规模,而不需要代码、客户端感知,同时也应支持动态减小消息系统规模
峰值处理能力 一个消息系统所处理的数据规模很可能是波动的,那么消息系统就应具备峰值处理的能力,当峰值降低时,又可释放资源,避免不必要的资源浪费
消息解耦 对外,消息系统的接口应尽可能统一且稳定;而消息系统内部的组件之间的接口应具备最低的耦合度。


Kafka架构

Broker:Kafka集群中的一个或多个服务器
Producer:负责发送数据到Broker中
Consumer:负责接收存于Broker中的数据
Consumer Group:每个Consumer都属于特定的Consumer Group
Topic:发送到kafka中的消息是以topic的概念存在的,同一个topic的数据可能存在于同一个Broker中,也可能存在于不同的Broker中
Partition:分区,因为数据最终是要落盘的,所以Partition是物理存储上的概念,每一个topic可以有一个或多个分区。

Kafka拓扑

以下这个图,大体说明了kafka的处理拓扑,由不同的Producer负责生产数据,存于Broker中,Broker的个数是可以指定的,一个时就是单机版kafka了,启用多个时,kafka以集群模式工作。而Consumer则负责消费数据,对同一个topic,可以有多个Consumer进行消费,消费的速度、数量都是可以控制的。而Zookeeper的工作贯穿全程,后面会专门分析Zookeeper在Kafka中都担当了什么角色。
Kafka架构
Kafka架构中的每一个组件,都起了至关重要的角色,正是因为这些组件优秀的设计,让kafka成为一个高吞吐、轻量级的消息系统。接下来我们会依次分析Kafka的一些重要设计或者组件。

关于Push & Pull

如果一直在往消息系统中发送数据,但是消息系统处理性能遇到了问题,或是其他原因,无法让消费者很快的消费走,就有可能造成网络阻塞、拒绝服务。所以Kafka为了解决生产者和消费者速率不同的问题,采用传统的Push和Pull的方式去处理数据。 Producer向 broker push消息,然后由 Consumer 从 Broker 中 Pull 消息。这样 Consumer 可以自主控制消费的速率和方式。

关于Topic & Partition

在Kafka中,所有的消息是以topic的概念存在的,每一类消息属于同一个 Topic ,而Topic的数据最终是要写入磁盘,如果一个 Topic 对应一个文件,即使 Kafka 的处理性能很高,但最终这个文件所在的机器 I/O 将会成为这个 Topic 的性能瓶颈。
为了解决这个问题, Partition 就需要引入了,一个消息可以分成多个 Partition ,每个 Partition 对应一个文件。这种方法可以极大提高吞吐率

从下图可见,当创建topic_test这个topic时指定了partition为3,所以会在kafka log下自动生成三个文件夹:
Partition文件
而每条消息不是随机插入到 Partition 中,而是都被 append 到该Partition中,这属于顺序写磁盘,因此效率非常高,是 Kafka 高吞吐率的另一个很重要的保证
Partition写入数据
只要数据一直存在文件中,消费者可以随时消费走,但是文件不能够持续增大,Kafka提供了删除旧消息的两种策略,一个是基于时间,一个是基于Partition大小,具体参数在Kafka提供两种策略删除旧数据。一是基于时间,二是基于Partition文件大小,可以通过修改配置文件server.properties实现。
要留意到一点,文件的大小和消费的速度是无关的,是因为kafka专门有地方存储每个Consumer的消费位置(即offset,偏移,这个会在后面章节详细提到),所以Kafka消费数据的时间复杂度始终为O(1),删除旧数据不会影响消费性能。
由此可见,Broker是无状态的,不会记录消息的消费情况,Broker不需要保证同一个Consumer Group中的不同消费者不会重复消费,所以不需要锁的机制,这也是Kafka高吞吐的保障。但是,因为Broker是无状态的,所以需要引入应答机制(acknowledgement)来确认消费成功。

关于Producer

默认情况下,每个Topic的Partition的个数为1,可以在配置文件下修改,全局生效,也可以在创建topic时,通过命令手动指定partition个数。而具体每条消息落在哪个Partition上,是Producer发送消息时透过key和partition个数计算出来的。
当Producer向broker发送消息时,一旦这条消息被commit之后,就不会丢失了,但是如果发送数据给broker后,遇到网络问题而造成通信中断,那Producer就无法判断该条消息是否已经commit了,这时候就需要一些发送方式来保证:

  1. At most once 消息可能会丢,但绝不会重复传输,这种方式下,Producer采用异步的方式。Producer会尝试重新发送message.send.max.retries(默认值为3)次后记录该异常并继续发送后续数据,这会造成数据丢失并且用户只能通过日志发现该问题。
  2. At least one 消息绝不会丢,但可能会重复传输。Producer使用同步模式。Producer会在尝试重新发送message.send.max.retries(默认值为3)次后抛出Exception,用户可以选择停止发送后续数据也可选择继续选择发送。而前者会造成数据的阻塞,后者会造成本应发往该Broker的数据的丢失。
  3. Exactly once 每条消息肯定会被传输一次且仅传输一次,很多时候这是用户所想要的。

关于Consumer Group

Consumer Group消费原则
同一Topic的一条消息只能被同一个Consumer Group内的一个Consumer消费,但多个Consumer Group可同时消费这一消息。
以一个topic,3个partition,两个Consumer Group举例,如下图所示:
Consumer Group

当想实现单播时,即这个数据只让某个consumer收到,那就在Consumer Group只设置多个consumer,每个consumer都消费不同的partition,如上图Consumer Group 0
当想实现广播时,就在每个Consumer Group只设置一个consumer,让所有的消费者在不同的group中

而partition的个数是可以动态修改的,每次修改完之后,都会触发消费者重新分配消费的Partition,叫做 Consumer Rebalance。


关于Offset

消费者在消费的过程中需要记录自己消费了多少数据,即消费位置信息。在Kafka中这个位置信息有个专门的术语:位移(offset)
由于要保存很多consumer的offset信息,必然引入复杂的数据结构,造成资源浪费。所以 Kafka选择了不同的方式:
每个consumer group管理自己的位移信息,那么只需要简单的一个整数表示位置就够了(记录自己group下都消费了哪些topic,每个topic消费到的offset是多少);
同时可以引入checkpoint机制定期持久化,简化了应答机制的实现,默认是定期帮你自动提交位移的(enable.auto.commit = true),你当然可以选择手动提交位移实现自己控制。
在老版本的位移是提交到zookeeper中的,但是zookeeper其实并不适合进行大批量的读写操作,尤其是写操作。所以现在的 Kafka 提供了另一种解决方案:增加__consumers_offsets 这个内部的topic,将offset信息写入这个topic中,摆脱对zookeeper的依赖。
__consumers_offsets 默认有50个分区,1个副本,根据hash算法,确认每个group在哪个分区存储offset。
__consumers_offsets

到此为止,介绍完毕了Kafka的基本功能,相信各位对Kafka的原理有了基本认识,那么Kafka又是如何实现高可用的呢,请参考另一篇文章:Kafka HA机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值