1、基本介绍(了解)
Kafka是一款消息队列的中间件产品, 来源于领英公司, 后期贡献给了Apache, 目前是Aapche旗下的顶级开源项目, 采用语言是Scala
官方地址: http://kafka.apache.org
kafka的特点:
-
可靠性:Kafka集群是分布式的,并且有多副本的机制。数据可以自动复制
-
可扩展性:Kafka集群可以灵活的调整,在线扩容
-
耐用性:Kafka数据保存在磁盘上面,数据并且有多副本的机制。数据持久化,而且可以一定程度上防止数据丢失
-
高性能:Kafka可以存储海量的数据,虽然是使用磁盘进行数据存储,但是Kafka有各种优化手段(例如:磁盘的顺序读写、零拷贝等)提高数据的读写速度(吞吐量)
2、Kafka的架构(掌握)
回顾HDFS文件存放流程:
Kafka的架构:
Kafka Cluster: Kafka 集群 broker: Kafka的节点 Topic: 主题/话题 partition: 分区,一个Topic会有多个Partition replication: 副本,副本个数不能够超过broker的个数 zookeeper: 负责Kafka集群管理,以及元数据管理 Producer: 生产者,负责生产数据 Consumer: 消费,负责消费数据
三、Kafka的集群搭建(操作)
1、软件安装
环境搭建,参考【Spark课程阶段_部署文档.doc】的9.1章节内容。
2、安装易错点
-
1- 配置文件中监听地址前面的注释,记得打开
-
2- 分发之后,记得要修改每个server.properties的 id 和 监听地址
-
3- 分发之后,记得source /etc/profile让环境变量生效
-
4- 没有启动zookeeper,或者仅仅启动了其中一台
-
5- 启动的时候server.properties中路径,不要写错了
3、Kafka的一键化启动脚本
注意:使用一键化脚本,也得需要先启动zookeeper
环境搭建,参考【Spark课程阶段_部署文档.doc】的8.3章节内容。
四、Kafka的相关使用(掌握)
1、Kafka的shell命令使用
Kafka本质上就是一个消息队列的中间件的产品,主要负责消息数据的传递。也就说学习Kafka 也就是学习如何使用Kafka生产数据,以及如何使用Kafka来消费数据
-
1- 创建Topic
./kafka-topics.sh --create --bootstrap-server node1:9092,:9092,:9092 --partitions 5 --replication-factor 2 --topic test01 参数说明: --create: 操作类型。这里是创建 --bootstrap-server: 指定Kafka集群的连接信息,就是在server.properties中配置的信息 --partitions 5: 配置Topic的分区数量,数量只要大于等于1就可以 --replication-factor 2: 配置每个分区的总的副本数。副本数不能超过集群的broker个数 --topic test01: 指定新建的Topic名称
./kafka-topics.sh --create --bootstrap-server node1:9092,node2:9092,node3:9092 --partitions 5 --replication-factor 4 --topic test03 创建test03不成功,会报错。因为创建的Topic的副本数是4,已经操作了Kafka集群的broker数量(有3个)
-
2- 查看所有Topic
./kafka-topics.sh --bootstrap-server node1:9092,node2:9092,node3:9092 --list 参数说明: --list: 操作类型。这里是列出对应的Kafka集群上可用的Topic列表 --bootstrap-server: 指定Kafka集群的连接信息,就是在server.properties中配置的信息
-
3- 查看具体Topic
./kafka-topics.sh --describe --bootstrap-server node1:9092,node2:9092,node3:9092 --topic test01 参数说明: --describe: 操作类型。查看具体Topic的信息 --bootstrap-server: 指定Kafka集群的连接信息,就是在server.properties中配置的信息 --topic test01: 指定需要查看的具体Topic名称。如果不加--topic该参数,会将集群中所有可用的Topic信息都输出出来
-
4- 模拟生产者Producer
./kafka-console-producer.sh --broker-list node1:9092,node2:9092,node3:9092 --topic test01 参数说明: --broker-list: 指定Kafka集群的连接信息,就是在server.properties中配置的信息 --topic test01: 指定需要将数据发送到的Topic名称
-
5- 模拟消费者Consumer
./kafka-console-consumer.sh --bootstrap-server node1:9092,node2:9092,node3:9092 --topic test01 参数说明: --bootstrap-server: 指定Kafka集群的连接信息,就是在server.properties中配置的信息 --topic test01: 指定需要消费消息的Topic名称 --from-beginning: 从Topic最开始的消息进行消费 latest: 从Topic最新的消息进行消费 --group: 指定消费组名称。一个消费组里面包含多个消费者,同一个Topic的同一条数据,只会被消费组中的某一个消费者所消费。 --max-messages: 指定消费的消息的最大条数 在实际企业中,一般是latest和--max-messages配合使用。因为实际工作,一个Topic的数据量是非常大的,避免造成机器宕机。
-
6- 修改Topic
./kafka-topics.sh --alter --bootstrap-server node1:9092,node2:9092,node3:9092 --topic test01 --partitions 10 修改分区数: 只能增大分区数 修改副本数: 既不能增大,也不能减小副本数
./kafka-topics.sh --alter --bootstrap-server node1:9092,node2:9092,node3:9092 --topic test01 --partitions 5 尝试减小分区,会报如下错误:
./kafka-topics.sh --alter --bootstrap-server node1:9092,node2:9092,node3:9092 --topic test01 --replication-factor 3 --partitions 15 尝试修改副本数,会报如下错误:
-
7- 删除Topic
./kafka-topics.sh --delete --bootstrap-server node1:9092,node2:9092,node3:9092 --topic test01 参数说明: --delete: 指定操作类型,这里是删除Topic --bootstrap-server: 指定Kafka集群的连接信息,就是在server.properties中配置的信息 --topic test01: 指定需要将数据发送到的Topic名称
2、Kafka基准测试(了解)
Kafka的基准测试, 主要是用于测试Kafka集群的吞吐量, 每秒钟最大可以生产多少条数据, 以及每秒钟最大可以消费多少条数据
2.1 测试生产的效率
-
1- 创建Topic
./kafka-topics.sh --create --bootstrap-server node1:9092,node2:9092,node3:9092 --partitions 3 --replication-factor 2 --topic test01
-
2- 执行生产测试命令: 测试后,会增加4GB磁盘占用
./kafka-producer-perf-test.sh --topic test01 --num-records 500000 --throughput -1 --record-size 1000 --producer-props bootstrap.servers=node1:9092,node2:9092,node3:9092 acks=1
-
3- 测试结果
2.2 测试消费的效率
-
1- 执行消费测试命令
./kafka-consumer-perf-test.sh --broker-list node1:9092,node2:9092,node3:9092 --topic test01 --fetch-size 1048576 --messages 500000
-
2- 测试结果:
3、Kafka的Python API的操作
准备工作:在node1的节点上安装一个python用于操作Kafka的库
安装命令: python -m pip install kafka-python -i https://pypi.tuna.tsinghua.edu.cn/simple API使用的参考文档: https://kafka-python.readthedocs.io/en/master/usage.html#kafkaproducer
3.1 完成生产者代码
import time from kafka import KafkaProducer # 快捷键: main + 回车 if __name__ == '__main__': print("Kafka生产者Demo") # 创建Producer生产者 """ bootstrap_servers:Kafka集群连接信息 """ producer = KafkaProducer(bootstrap_servers=['node1:9092','node2:9092','node3:9092']) for i in range(0,20): # 同步发送 """ topic:指定要生产的Topic名称 value:生产的消息内容。类型需要是bytes """ # metadata = producer.send(topic='test01',value=f'kafka_{i}'.encode('UTF-8')).get() # print(metadata.topic) # 获取生产的Topic主题 # print(metadata.partition) # 获取生产的分区信息 # print(metadata.offset) # 获取生产的偏移量 # 异步发送 """ 异步发送特点: 1、将消息发送出去以后,无法获取metadata元数据信息。如果尝试进行获取,会报'FutureRecordMetadata' object has no attribute 'topic' 2、异步发送,会首先将数据发到本地的缓存队列里面。当缓存队列里面的数据量或者缓存时间超过一定阈值以后,就会自动的批量发送到Kafka """ producer.send(topic='test01', value=f'kafka_async2_{i}'.encode('UTF-8')) # time.sleep(1) # 关闭资源 producer.close()
3.2 完成消费者代码
from kafka import KafkaConsumer # 快捷键: main + 回车 if __name__ == '__main__': print("Kafka消费者Demo") # 创建消费者 """ test01:指定要消费的Topic名称 bootstrap_servers:Kafka集群连接信息 group_id:消费者所属的消费组名称 """ consumer = KafkaConsumer( 'test01', bootstrap_servers=['node1:9092','node2:9092','node3:9092'], group_id='g_1' ) # 对消费到的数据进行打印输入 for message in consumer: value = message.value.decode('UTF-8') key = message.key topic = message.topic partition = message.partition offset = message.offset print(f'消息的topic{topic}, partition{partition}, offset{offset}, key{key}, value{value}') # 释放资源 consumer.close()
五、Kafka的核心原理(熟悉)
1、Topic的分区和副本机制
分区有什么用呢?
1-解决单台节点容量有限的问题: 每一台服务器的硬盘资源始终是有上限的。一个Topic分成多个分区之后,可以使用多个服务器节点的存储资源,提供更多的数据存放能力。 2-增加Topic对数据的吞吐量: 通过分区操作,将一个Topic分成了多个分区,多个分区分布在不同的节点上面,从而提供更加高效的数据读写请求 分区的数量有没有限制? 没有限制,和集群中Broker节点个数没有半毛钱关系。但是实际工作中,推荐分区数量不要超过集群总节点个数的3倍,这只是一个经验值。
副本有什么用呢?
提高数据的可靠性: 副本越多,数据就越安全。冗余(相同)的数据就越多 副本的数量有没有限制? 有限制,最多和集群中Broker节点个数一致。但是实际工作中,根据数据的重要程度,推荐副本的数量在1-3之间。如果数据相对不太重要,可以配置少一些的副本数量;如果数据相对重要,可以配置多一些的副本数量。
2、消息存储和查询机制
2.1 存储机制
1-Kafka中数据存放位置: 在server.properties中配置的log.dirs=/export/server/kafka/data 2-data目录下,文件夹的命名规则如下 Topic名称-分区编号 3-segment中文件名称命名规则如下 文件名称中的数字,表示该文件中第一条数它的offset(偏移量)
Kafka是将数据存放在磁盘上面,磁盘的存放路径在server.properties中配置的log.dirs=/export/server/kafka/data来进行指定。 在对应的目录下,以 Topic名称-分区编号 创建一个目录。 在目录中放在消息数据,主要有两个核心文件,一个是xxx.index文件,另外一个是xxx.log文件 index文件: 存储索引数据,主要作用是加快查询log文件中数据的速度 log文件: 存放实际的消息数据 请问: 消息数据是一直不断的往一个log文件写呢,还是会分成多个文件来存储? 答: 会分成多个文件存储。当log文件的大小达到1GB的时候,就会滚动形成新的文件。同时配套的index文件也会跟着滚动生成。 请问: 为什么要分成不同的文件存储? 答: 1-一个文件过大,打开和关闭文件需要消耗大量的系统资源 2-一个文件过大,在里面查找具体数据的时候,非常耗时 3-Kafka只是对数据做临时的存储,而不是永久存储,所以Kafka会定时的将过期数据删除掉。当消息超过一定时间以后,Kafka会认为数据失效,就会被Kafka删除。数据默认的保留时间是168小时(7天),是在server.properties中通过log.retention.hours=168配置参数指定
index文件内容基本结构:
2.2 查询的机制
查询步骤: 1-首先先确定要读取的这个Offset(偏移量)在哪个segment片段中 2-查询这个segment片段的index文件,根据offset确定这个消息在log文件的什么位置,也就是确定消息的物理偏移量 3-读取log文件,查询对应范围内的数据即可(底层是基于磁盘顺序查询) 4-获取最终的消息数据
扩展内容: 磁盘的读写中,有两种方案:随机读写 和 顺序读写。顺序读写的速度会更快
3、Kafka中生产者数据分发策略
何为生产者的数据分发策略呢?
指的就是生产者生产的数据到达Broker的某一个Topic之后,这个消息最终应该写入到Topic的哪一个分区上面,这就是数据的分发策略
分发策略如下这些:
【
-
1- 随机分发策略:在Python的客户端支持,但是在Java中不支持
-
2- 指定分区策略:发送数据的时候,直接明确指定要将数据发到哪个分区。支持
-
3- Hash取模策略:对数据按照Key进行Hash取模,然后分发。支持
-
4- 轮询策略:在2.4及以上版本,已经更改为粘性分发策略。在Python的客户端不支持,但是在Java中支持
-
5- 自定义分区策略:支持
】
-
1- 随机分发策略
def send(self, topic, value=None, key=None, headers=None, partition=None, timestamp_ms=None): 当在发送数据的时候, 如果只传递了topic 和 value, 那么此时就采用随机策略 在kafka中, 专门提供了一个默认的分发数据的类: DefaultPartitioner def __call__(cls, key, all_partitions, available): """ 如果 key为 null, 那么随机返回一个分片的编号 """ if key is None: if available: return random.choice(available) return random.choice(all_partitions) # 后续的代码 当没有key的时候,压根就执行不到 idx = murmur2(key) idx &= 0x7fffffff idx %= len(all_partitions) return all_partitions[idx]
-
2- 指定分区策略
def send(self, topic, value=None, key=None, headers=None, partition=None, timestamp_ms=None): 当在发送数据的时候, 如果指定了partition参数, 表示的采用指定分区的方案, 分区的编号从0开始 当指定了partition的参数后, 与DefaultPartitioner没有任何的关系
-
3- Hash取模策略
def send(self, topic, value=None, key=None, headers=None, partition=None, timestamp_ms=None): 当在发送数据的时候, 如果传递了topic 和 value 以及key的时候, 那么此时就是采用hash取模策略 注意: 相同key的返回的hash值是一致的, 同样对应分区也是同一个 在kafka中, 专门提供了一个默认的分发数据的类: DefaultPartitioner def __call__(cls, key, all_partitions, available): """ 如果 key为 null, 那么随机返回一个分片的编号 """ if key is None: if available: return random.choice(available) return random.choice(all_partitions) # 当有key的时候 , 执行下列代码. 此处的代码其实就是hash取模的方案 idx = murmur2(key) idx &= 0x7fffffff idx %= len(all_partitions) return all_partitions[idx]
-
4- 自定义分区策略: 参考DefaultPartitioner 它怎么写你就怎么写
第一步: 创建一个自定义分区类 from __future__ import absolute_import import random from kafka.vendor import six class MyPartitioner(object): # 第二步: 创建一个方法: __call__ 传入几个参数 参数1 表示key值 参数2 所有分区 参数3 可用的分区 def __call__(self, key, all_partitions, available): # 第三步: 自定义实现分区策略 #第四步: 通过return 返回响应的分区 return all_partitions[i] 或者 available[i] 第五步: 将自己写的分区配置到生产者中 kafkaProducer = KafkaProducer( bootstrap_servers=['node1:9092', 'node2:9092', 'node3:9092'], acks = -1, partitioner = MyPartitioner() )
Java中的 轮询分发策略 和 粘性分发策略介绍:
轮询分发策略: 在Kafka的老版本中存在的一种分发策略,当生产数据的时候,只有value但是没有key的时候,采用轮询。 优点: 可以保证每个分区拿到的数据基本是一样,因为是一个一个的轮询的分发 缺点: 如果采用异步发送方式,意味着一批数据发送到broker端,由于是轮询策略,会将这一批数据拆分为多个小的批次,分别再写入到不同的分区里面去,写入进去以后,每个分区都会给予响应,会影响写入效率。 粘性分发策略: 在Kafka新版本中存在的一种分发策略。当生产数据的时候,只有value但是没有key的时候,采用粘性分发策略 优点: 在发送数据的时候,首先会随机的选取一个分区,然后尽可能将数据分发到这个分区上面去,也就是尽可能粘着这个分区。该分发方式,在异步发送的操作中,效率比较高。 弊端: 在数据发送特别快的时候,可能会导致某个分区的数据比其他分区数据多很多,造成大量的数据集中在一个分区上面