Kafka的基本介绍

1、基本介绍(了解)

Kafka是一款消息队列的中间件产品, 来源于领英公司, 后期贡献给了Apache, 目前是Aapche旗下的顶级开源项目, 采用语言是Scala

官方地址: http://kafka.apache.org

kafka的特点:

  • 可靠性:Kafka集群是分布式的,并且有多副本的机制。数据可以自动复制

  • 可扩展性:Kafka集群可以灵活的调整,在线扩容

  • 耐用性:Kafka数据保存在磁盘上面,数据并且有多副本的机制。数据持久化,而且可以一定程度上防止数据丢失

  • 高性能:Kafka可以存储海量的数据,虽然是使用磁盘进行数据存储,但是Kafka有各种优化手段(例如:磁盘的顺序读写、零拷贝等)提高数据的读写速度(吞吐量)

2、Kafka的架构(掌握)

回顾HDFS文件存放流程:

1687137638475

Kafka的架构:

1687139789833

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个)

1687146487916

  • 2- 查看所有Topic

./kafka-topics.sh --bootstrap-server node1:9092,node2:9092,node3:9092 --list
​
参数说明:
    --list: 操作类型。这里是列出对应的Kafka集群上可用的Topic列表
    --bootstrap-server: 指定Kafka集群的连接信息,就是在server.properties中配置的信息

1687144880025

  • 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信息都输出出来

1687146198857

  • 4- 模拟生产者Producer

./kafka-console-producer.sh --broker-list node1:9092,node2:9092,node3:9092 --topic test01
​
参数说明:
    --broker-list: 指定Kafka集群的连接信息,就是在server.properties中配置的信息
    --topic test01: 指定需要将数据发送到的Topic名称

1687146915401

  • 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
​
尝试减小分区,会报如下错误:

1687148269797

./kafka-topics.sh --alter --bootstrap-server node1:9092,node2:9092,node3:9092 --topic test01 --replication-factor 3 --partitions 15
​
尝试修改副本数,会报如下错误:

1687148185641

  • 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

1687157624933

  • 3- 测试结果

1687158084644

2.2 测试消费的效率
  • 1- 执行消费测试命令

./kafka-consumer-perf-test.sh --broker-list node1:9092,node2:9092,node3:9092 --topic test01 --fetch-size 1048576 --messages 500000

1687158135590

  • 2- 测试结果:

1687158207404

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

1687158401750

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()

1687161080095

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()

1687161776676

五、Kafka的核心原理(熟悉)

1、Topic的分区和副本机制

分区有什么用呢?

1-解决单台节点容量有限的问题: 每一台服务器的硬盘资源始终是有上限的。一个Topic分成多个分区之后,可以使用多个服务器节点的存储资源,提供更多的数据存放能力。
2-增加Topic对数据的吞吐量: 通过分区操作,将一个Topic分成了多个分区,多个分区分布在不同的节点上面,从而提供更加高效的数据读写请求
​
分区的数量有没有限制?
没有限制,和集群中Broker节点个数没有半毛钱关系。但是实际工作中,推荐分区数量不要超过集群总节点个数的3倍,这只是一个经验值。

副本有什么用呢?

提高数据的可靠性: 副本越多,数据就越安全。冗余(相同)的数据就越多
​
​
副本的数量有没有限制?
有限制,最多和集群中Broker节点个数一致。但是实际工作中,根据数据的重要程度,推荐副本的数量在1-3之间。如果数据相对不太重要,可以配置少一些的副本数量;如果数据相对重要,可以配置多一些的副本数量。

2、消息存储和查询机制

2.1 存储机制

1687165911010

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文件内容基本结构:

1687166752520

2.2 查询的机制

1687167408007

查询步骤:
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的时候,采用粘性分发策略
    优点: 在发送数据的时候,首先会随机的选取一个分区,然后尽可能将数据分发到这个分区上面去,也就是尽可能粘着这个分区。该分发方式,在异步发送的操作中,效率比较高。
    弊端: 在数据发送特别快的时候,可能会导致某个分区的数据比其他分区数据多很多,造成大量的数据集中在一个分区上面
  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值