一、kafka的定义
- Kafka 是一个分布式的基于发布/订阅模式的消息队列(MQ,Message Queue),主要应用于大数据实时处理领域。
- Kafka 是最初由 Linkedin 公司开发,是一个分布式、支持分区的(partition)、多副本的(replicar 协调的分布式消息中间件系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景,比如基于 hadoop 的批处理系统、低延迟的实时系统、Spark/Flink 流式处理引擎,nginx 访问日志,消息服务等等,用 scala 语言编写
kafka的特性
- 高吞吐量、低延迟 : kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒。
- 可扩展性(分布式): kafka集群支持热扩展
- 持久性、可靠性: 消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
- 容错性: 允许集群中节点失败
- 高并发: 支持数千个客户端同时读写。
1、为什么需要消息队列(MQ)⭐⭐⭐
主要原因是由于在高并发环境下,同步请求来不及处理,请求往往会发生阻塞。比如大量的请求并发访问数据库,导致行锁表锁,最后请求线程会堆积过多,从而触发 too many connection 错误,引发雪崩效应。
我们使用消息队列,通过异步处理请求,从而缓解系统的压力。消息队列常应用于异步处理,流量削峰,应用解耦,消息通讯等场景。
当前比较常见的 MQ 中间件有 ActiveMQ、RabbitMQ、RocketMQ、Kafka 等。
2、使用消息队列的好处⭐⭐⭐
解耦
- 允许你独立的扩展或修改两边的处理过程,只要确保他们遵守同样的接口约束。
可恢复性
- 系统的一部分组件失效时,不会影响到整个系统,消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
缓冲
- 有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
灵活性和峰值处理能力
- 访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
异步通信
- 很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
二、消息队列的模式
1、点对点消息传递模式
(一对一,消费者主动拉取数据,消息收到后消息清除)
-
消息生产者生产消息发送到消息队列中,然后消息消费者从消息队列中取出并且消费消息。
-
消息被消费以后,消息队列中不再有存储,所以消息消费者不可能消费到已经被消费的消息。
-
消息队列支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
-
该模式即使有多个消费者同时消费数据,也能保证数据处理的顺序。
2、发布订阅消息传递模式⭐⭐⭐
(一对多,又叫观察者模式,消费者消费数据之后不会清除消息)
- 消息生产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到 topic 的消息会被所有订阅者消费。
- 发布/订阅模式是定义对象间一种一对多的依赖关系,使得每当一个对象(目对标象)的状态发生改变,则所有依赖于它的对象(观察者对象)都会得到通知并自动更新。
三、kafka系统基础架构
1、Producer(生产者)
- 消息的生产者,是消息的入口
2、Broker(实例)
- 一台kafka服务器就是一个broker,一个集群由多个broker组成。一个broker可以容纳多个topic(主题)
3、Topic(主题)
- 消息的主题,可以理解成消息的分类,kafka获取到的数据就是按照不同的类型存储在不同的topic主题中。
- topic主题中有很多的分区。
4、Partition(分区)
- 为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分割为一个或多个 partition,每个 partition 是一个有序的队列。
- Kafka 只保证 partition 内的记录是有序的,而不保证 topic 中不同 partition 的顺序。
每个 topic 至少有一个 partition,当生产者产生数据的时候,会根据分配策略选择分区,然后将消息追加到指定的分区的队列末尾。
##Partation 数据路由规则:
1.指定了 patition,则直接使用;
2.未指定 patition 但指定 key(相当于消息中某个属性),通过对 key 的 value 进行 hash 取模,选出一个 patition;
3.patition 和 key 都未指定,使用轮询选出一个 patition。
每条消息都会有一个自增的编号,用于标识消息的偏移量,标识顺序从 0 开始。
每个 partition 中的数据使用多个 segment 文件存储。
如果 topic 有多个 partition,消费数据时就不能保证数据的顺序。严格保证消息的消费顺序的场景下(例如商品秒杀、 抢红包),需要将 partition 数目设为 1。
broker 存储 topic 的数据:
- 如果某 topic 有 N 个 partition,集群有 N 个 broker,那么每个 broker 存储该 topic 的一个 partition。
- 如果某 topic 有 N 个 partition,集群有 (N+M) 个 broker,那么其中有 N 个 broker 存储 topic 的一个 partition, 剩下的 M 个 broker 不存储该 topic 的 partition 数据。
- 如果某 topic 有 N 个 partition,集群中 broker 数目少于 N 个,那么一个 broker 存储该 topic 的一个或多个 partition。在实际生产环境中,尽量避免这种情况的发生,这种情况容易导致 Kafka 集群数据不均衡。
分区的原因:
- 便于在集群中扩展,每个Partition可以通过调整以适应它所在的机器,而一个topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了。
- 可以提高并发,因为可以以Partition为单位读写了。
(1)Replica(副本)
- 每一个分区都有多个副本,副本的作用就是备份数据。一个Topic的每个分区都有若干个副本,一个leader和若干个follower。
- 当主分区故障后,副本进行顶替它的位置。
(2)Leader(领导者)
- 每个分区有多个副本,其中有且仅有一个作为leader,leader是当前负责数据读写的分区。
(3)follower(追随者)
- follower跟随leader,所有请求都通过leader路由,数据变更会广播给所有follower,follower和leader保持数据同步,follower只负责备份,不负责数据的读写。
- 如果leader故障,则从follower中选举出一个新的leader。
- 当follower挂掉,卡主或者同步太慢,leader会把这个follower从集群列表中删除,重新创建一个follower。
(4) producer
- 生产者即数据的发布者,该角色将消息 push 发布到 Kafka 的 topic 中。
- broker 接收到生产者发送的消息后,broker 将该消息追加到当前用于追加数据的 segment 文件中。
- 生产者发送的消息,存储到一个 partition 中,生产者也可以指定数据存储的 partition。
(5)Consumer
消费者可以从 broker 中 pull 拉取数据。消费者可以消费多个 topic 中的数据。
(6)Consumer Group(CG)
- 消费者组,由多个 consumer 组成。
- 所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。可为每个消费者指定组名,若不指定组名则属于默认的组。
- 将多个消费者集中到一起去处理某一个 Topic 的数据,可以更快的提高数据的消费能力。
- 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费,防止数据被重复读取。
- 消费者组之间互不影响。
(7)offset 偏移量
- 可以唯一的标识一条消息
- 偏移量决定读取数据的位置,不会有线程安全的问题,消费者通过偏移量来决定下次读取的消息(即消费位置)
- 消费被消费之后,并不会被删除,这样多个业务就可以重复使用kafka的消息。
- 某一个业务也可以通过修改偏移量达到重新读取消息的目的,偏移量由用户控制。
- 消息最终还是会被删除,默认生命周期为1周(168小时)。
(8)Zookeeper
kafka集群依赖zookeeper来存储meta(变化)信息。
- 由于 consumer 在消费过程中可能会出现断电宕机等故障,consumer 恢复后,需要从故障前的位置的继续消费,所以 consumer 需要实时记录自己消费到了哪个 offset,以便故障恢复后继续消费。
- Kafka 0.9 版本之前,consumer 默认将 offset 保存在 Zookeeper 中;从 0.9 版本开始,consumer 默认将 offset 保存在 Kafka 一个内置的 topic 中,该 topic 为 __consumer_offsets。
- zookeeper的作用就是,生产者push数据到kafka集群,就必须要找到kafka集群的节点在哪里,这些都是通过zookeeper去寻找的。消费者消费哪一条数据,也需要zookeeper的支持,从zookeeper获得offset,offset记录上一次消费的数据消费到哪里,这样就可以接着下一条数据进行消费。
四、kafka 工作流程及文件存储机制
- Kafka 中消息是以 topic 进行分类的,生产者生产消息,消费者消费消息,都是面向 topic 的。
- topic 是逻辑上的概念,而 partition 是物理上的概念,每个 partition 对应于一个 log 文件,该 log 文件中存储的就是 producer 生产的数据。
- Producer 生产的数据会被不断追加到该 log 文件末端,且每条数据都有自己的 offset。 消费者组中的每个消费者,都会实时记录自己消费到了哪个 offset,以便出错恢复时,从上次的位置继续消费。
- 由于生产者生产的消息会不断追加到 log 文件末尾,为防止 log 文件过大导致数据定位效率低下,Kafka 采取了分片和索引机制,将每个 partition 分为多个 segment。每个 segment 对应两个文件:“.index” 文件和 “.log” 文件。这些文件位于一个文件夹下,该文件夹的命名规则为:topic名称+分区序号。例如,test 这个 topic 有三个分区, 则其对应的文件夹为 test-0、test-1、test-2。
-
index 和 log 文件以当前 segment 的第一条消息的 offset 命名。
-
“.index” 文件存储大量的索引信息,“.log” 文件存储大量的数据,索引文件中的元数据指向对应数据文件中 message 的物理偏移地址。
五、数据可靠性保证
为保证 producer 发送的数据,能可靠的发送到指定的 topic,topic 的每个 partition 收到 producer 发送的数据后, 都需要向 producer 发送 ack(acknowledgement 确认收到),如果 producer 收到 ack,就会进行下一轮的发送,否则重新发送数据。
六、数据一致性问题
LEO:指的是每个副本最大的 offset;
HW:指的是消费者能见到的最大的 offset,所有副本中最小的 LEO。
(1)follower 故障
follower 发生故障后会被临时踢出 ISR(Leader 维护的一个和 Leader 保持同步的 Follower 集合),待该 follower 恢复后,follower 会读取本地磁盘记录的上次的 HW,并将 log 文件高于 HW 的部分截取掉,从 HW 开始向 leader 进行同步。等该 follower 的 LEO 大于等于该 Partition 的 HW,即 follower 追上 leader 之后,就可以重新加入 ISR 了。
(2)leader 故障
leader 发生故障之后,会从 ISR 中选出一个新的 leader, 之后,为保证多个副本之间的数据一致性,其余的 follower 会先将各自的 log 文件高于 HW 的部分截掉,然后从新的 leader 同步数据。
注:这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。
七、ack 应答机制
对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没必要等 ISR 中的 follower 全部接收成功。所以 Kafka 为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡选择。
当 producer 向 leader 发送数据时,可以通过 request.required.acks 参数来设置数据可靠性的级别:
- 0:这意味着producer无需等待来自broker的确认而继续发送下一批消息。这种情况下数据传输效率最高,但是数据可靠性确是最低的。当broker故障时有可能丢失数据。
- 1(默认配置):这意味着producer在ISR中的leader已成功收到的数据并得到确认后发送下一条message。如果在follower同步成功之前leader故障,那么将会丢失数据。
- -1(或者是all):producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。但是如果在 follower 同步完成后,broker 发送ack 之前,leader 发生故障,那么会造成数据重复。
三种机制性能依次递减,数据可靠性依次递增。
注:在 0.11 版本以前的Kafka,对此是无能为力的,只能保证数据不丢失,再在下游消费者对数据做全局去重。在 0.11 及以后版本的 Kafka,引入了一项重大特性:幂等性。所谓的幂等性就是指 Producer 不论向 Server 发送多少次重复数据, Server 端都只会持久化一条。
总结:
生产者要推送到kafka集群需要先通过zookeeper确定kafka的位置,消费者消费的数据到哪里也要根据数据在存储zookeeper上的offset,来确定offset偏移量记录上一条消息者消费的数据位置,以便在故障恢复后可以接着下一次数据继续消费
几个kafka服务器就是几个broker,生成推送数据到topic,topic可以被分区多个partition,一个partition可以有多个relica,relica副本可以是一个leader和多个follower,leader负责数据的读写,follower仅对数据进行备份。消费者面向topic进行数据的消费。