Kafka 核心知识

1. 消息队列

1.1 消息队列定义

消息队列(MessageQueue):是一种异步的服务间通信方式,是分布式系统中重要的组件,主要解决应用耦合异步消息流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。

简单点说:消息队列MQ用于实现两个系统之间或者两个模块之间传递消息数据时,实现数据缓存

1.2 消息队列概述

  • 生产者: 负责往消息队列中写数据

  • 消费者: 负责从消息队列中读数据

  • 消息队列: 临时存放两个系统之间需要传递的数据

  • 主题: 数据的分类,用于区分消息队列中不同的业务的数据

  • 订阅发布模式:
    生产者往消息队列中生产数据,将数据写入对应的主题中;消费者可以订阅主题,如果主题中出现新的数据,消费就可以立即消费,消费成功以后,不会立即主动删除数据。
    特点:一个消费者可以订阅多个主题,一个主题可以被多个消费者订阅

消息队列的优点与缺点:

优点缺点
实现了架构解耦架构运维更加复杂,一旦消息队列故障,整个系统全部崩溃
实现异步,提高传输性能数据保证更加复杂
限流削峰

2. Kafka概念

2.1 Kafka的定义

Kafka: 是基于订阅发布模式分布式实时消息队列系统。

2.2 基础角色

在这里插入图片描述

  1. Broker:Kafka是一个分布式集群,多台机器构成,每台Kafka的节点就是一个Broker。
    • Broker:就是Kafka节点,负责 存储 Kafka中的 数据 提供 读写
    • 进程:Kafka
    • 架构:公平分布式主从架构,基于Zookeeper选举主节点
    • 主:Broker【也叫 Controller】是Kafka主节点,负责管理Kafka的节点、Topic、元数据,负责存储数据,对外提供读写
    • 从:Broker:负责存储数据,对外提供读写
    • Kafka 3.0版本新特性:可以摆脱ZK,独立运行主从节点了
  2. Producer:生产者
    • 功能:负责往Kafka写数据的客户端
  3. Consumer:消费者
    • 功能:负责从Kafka中读取数据
  4. Consumer Group:消费者组,Kafka的订阅发布模式中必须以消费者组的形式从Kafka中消费数据
    • 任何一个消费者必须属于某一个消费者组
    • 一个消费者组中可以有多个消费者:多个消费者共同并行消费数据,提高消费性能
    • 消费者组中多个消费者消费的数据是不一样的
    • 整个消费者组中所有消费者消费的数据加在一起是一份完整的数据
  5. zk功能:
    • 辅助选举Controller:启动时所有Broker都会到ZK中创建临时节点,谁创建成功谁就是主节点
    • 存储Kafka元数据
    • Kafka 3.0版本新特性:可以摆脱ZK,独立运行了

2.3 主题与分区(Topic & Partition)

在这里插入图片描述

  1. Topic:数据主题,用于区分不同的数据,对数据进行分类
    • Kafka是分布式存储,所以Topic也是分布式的,Topic是Kafka中分布式的数据存储对象,写入Topic的数据会分布式的存储在Kakfa中
  2. Partition:数据分区,用于实现Topic的分布式存储,对Topic的数据进行划分
    • 每个Topic都可以对应多个分区,每个分区存储在不同的Kafka节点Broker上。如上图:Topic名称为T1,T1有三个分区:P0、P1、P2。
    • Kafka中一个分区的副本个数最多只能等于机器的个数
    • Kafka将一个分区的多个副本,(由Controller即主broker)划分为两种角色(Leader与Follower)
      • Leader副本: 负责对外提供读写,生产者和消费者只对leader副本进行读写
      • Follower副本: 与Leader同步数据,如果leader故障,从follower选举新的leader副本对外提供读写
    • Kafka的主节点【controller】根据机器的健康状态、数据的完整性来选择Leader和Follower副本
HDFSKafka
节点NameNode + DataNode:普通分布式主从架构Broker:公平分布式主从架构(根据zk选举划分成 Leader 与 Follower)
对象文件Topic
划分Block块Partition分区
规则按照大小:BlockSize=128M自己决定
安全副本机制:默认3份副本机制:自己决定

2.4 分片与偏移量(Segment & Offset)

在这里插入图片描述

  1. Segment:对每个分区的数据进行了更细的划分,用于存储Kafka中的数据文件并记录索引
    • 先写入的数据会生成一对Segment文件
    • 每个Segment对应两种【三个】文件
      • x.log:存储数据
      • x.index 与 x.timeindex:对应.log的文件的索引
    • Segment文件的名字就是这个Segment记录的offset的最小

在这里插入图片描述

  1. Offset:是每条数据在自己分区中的偏移量
    • 写入分区的顺序就是offset偏移量,Offset是分区级别的,每个分区的offset独立管理,都从0开始
    • Kafka写入数据按照KV来写入数据,数据存储的结构(offset, Key ,Value)
    • 消费者数据的读取都是按照Offset来读取数据

2.5 Kafka 概念总结

Kafka 概念介绍 &解释
ProducerKafka生产者,负责往Kakfa写数据的客户端
ConsumerKafka消费者,负责从Kafka读数据的客户端
ConsumerGroup消费者组,必须以消费组的形式才能消费,一个消费者组中可以包含多个消费者,任何一个消费者都必须属于某个消费者组
BrokerKafka节点,每个节点叫做一个Broker,通过zk选举主节点又叫Controller
Topic主题,用于区分不同的数据,实现数据分类,一个Topic可以对应多个分区
Partition分区,用于实现Kafka中Topic的分布式存储,每个分区可以分布在不同节点上,每个分区可以有多份
Replication副本,保证Kafka中分区的数据安全,副本个数小于等于节点个数,(由主节点Controller划分)两种角色:Leader,Follower
Segment分区文件段,将分区中的数据按一定规则细分,加快查询性能.由两种文件组成,.log数据文件和.index索引文件
KVKafka中写入数据也是KV结构
Offset每条数据在分区中的偏移量,第N条件数据的offset = N-1,用于保证消费者按照offset顺序消费,一致性

3. Kafka Shell命令

3.1 Topic 创建 & 查看

  1. 创建Topic
    kafka-topics.sh \
    --create \
    --topic zimo \
    --partitions 3 \
    --replication-factor 2 \
    --bootstrap-server node1:9092,node2:9092,node3:9092
    
  2. 查看Topic列表
    kafka-topics.sh \
    --list -bootstrap-server node1:9092,node2:9092,node3:9092
    
命令功能
–create创建
–topictopic名称
–partitions指定topic的分区数
–replication-factors指定分区副本个数
–bootstrap-server指定服务端地址
–list查看

3.2 Topic 描述 & 删除

  1. 描述Topic

    kafka-topics.sh \
    --describe \
    --topic bigdata01  \
    --bootstrap-server node1:9092,node2:9092,node3:9092
    
  2. 删除Topic

    kafka-topics.sh \
    --delete \
    --topic bigdata02  \
    --bootstrap-server node1:9092,node2:9092,node3:9092
    
命令功能
–describe描述
–delete删除

3.3 生产者 & 消费者

  1. 生产者
    kafka-console-producer.sh \
    --topic bigdata01 \
    --broker-list node1:9092,node2:9092,node3:9092
    
  2. 消费者
    kafka-console-consumer.sh \
    --topic bigdata01 \
    --bootstrap-server node1:9092,node2:9092,node3:9092 \
    --from-beginning
    
命令功能
–from-beginning从每个分区的最初开始消费,默认从最新的offset进行消费

3.4 生产者 & 消费者(py开发)

  1. 生产者
    from kafka import KafkaProducer
    """
        TODO1:构建Kafka生产者客户端对象,并且指定生产者的配置
            bootstrap_servers:指定的是服务端的地址
            acks:应答机制,用于决定生产者写入数据到Kafka是同步还是异步
                0:生产者生产一条数据写入Kafka,不用等待Kafka的回复,直接生产下一条【快,数据容易丢失】
                1:生产者生产一条数据写入Kafka,等待Kafka确保这个分区的Leader已经写入,就返回一个ack,生产者收到ack就发送下一条【性能和安全做了权衡】
                all/-1:生产者生产一条数据写入Kafka,等待Kafka确保这个分区的所有ISR副本都已经写入成功,再返回ack,生产者收到ack就发送下一条【安全,性能最差】
            retries:重试机制,如果生产者长时间没有收到ack,就认为数据丢失,会重新发送一份数据写入,直到收到ack
            
            应答机制+重试机制保证生产者生产数据不丢失
    """
    producer = KafkaProducer(bootstrap_servers=['node1:9092', 'node2:9092', 'node3:9092'], acks=1)
    """
        TODO2:实现通过生产者对象调用生产方法将数据生产写入Kafka中
            方法:send:用于将数据写入Kafka
            参数:
                topic:指定要写入的Topic
                key:指定写入的Key,决定了写入数据的分区规则
                value:指定写入的Value,也就是真正需要传递的数据
    """
    # 每次从0 - 9 中取一个值
    for i in range(0, 10):
        # 调用方法写入数据:topic、key、value
        rs = producer.send(topic="bigdata01", key=f"{i}".encode("UTF-8"), value=f"itcast{i}".encode("UTF-8"))
        # 调用方法写入数据:topic、value
        # rs = producer.send(topic="bigdata01", value=f"itcast{i}".encode("UTF-8"))
        # 调用方法写入数据:topic、key、value、partition
        # rs = producer.send(topic="bigdata01", key=f"{i}".encode("UTF-8"), value=f"itcast{i}".encode("UTF-8"), partition=0)
        # 获取返回结果
        metadata = rs.get(timeout=10)
        # 打印生产结果
        print(f"数据:itcast{i}  topic:{metadata.topic}  partition:{metadata.partition}  offset:{metadata.offset}")
    
    """
        TODO3:生产者不是直接将数据写入Kafka,而是先放到本地一个缓存中,当缓存达到一定大小或者超过一定的时间才会真正的写入Kafka
            flush:强制将本地缓存的数据提交到Kafka
    """
    producer.flush()
    # 如果报错 : SyntaxError: invalid syntax
    # 这是因为python3.7以上版本把async作为关键字了,与kafka冲突
    
  2. 消费者
    from kafka import KafkaConsumer
    """
        TODO:1-构建一个消费者对象,指定一些配置信息
            topics:需要消费的Topic
            group_id:指定当前消费者属于哪个消费者组,消费组的id
            bootstrap_servers:指定服务端地址
            auto_offset_reset:用于指定第一次消费从什么位置消费, earliest-最早位置,latest-最近位置
            enable_auto_commit:自动提交,开启后每个消费者会自动将自己负责的分区的commitoffset提交到__consumer_offsets这个topic中
                还有一个参数是提交时间间隔:自动提交是按照时间间隔自动提交,1000ms = 1s
    """
    consumer = KafkaConsumer(
        "bigdata01",
        group_id="cg1",
        bootstrap_servers=['node1:9092', 'node2:9092', 'node3:9092'],
        auto_offset_reset='earliest',
        enable_auto_commit=True
    )
    """
        TODO:2-实现消费,这是一个死循环,表示源源不断的去消费,如果没有数据数据就等待,有数据就立即消费,不停的
            message:表示消费到的每一条数据
            topic:当前数据所属的Topic
            partition:当前数据属于这个Topic哪个分区
            offset:当前数据属于这个分区的哪个offset
            key:获取数据中的key
            value:获取数据中的value
    """
    # 不断消费Kafka中的每条数据
    for message in consumer:
        # 从每条数据中获取所有信息
        topic = message.topic
        partition = message.partition
        offset = message.offset
        key = message.key
        value = message.value.decode("UTF-8")
        # 打印每条数据的内容
        print(f"Topic={topic}  Partition={partition} Offset={offset} Key={key} Value={value}")
    

4. Kafka 的消费过程

4.1 消费者消费数据的规则

  1. 首次消费:
    第一次消费规则【消费者组id在Kafka元数据中不存在】:由属性决定
    auto.offset.reset = latest | earliest
    latest:默认的值,从Topic每个分区的最新的位置开始消费
    earliest:从最早的位置开始消费,每个分区的offset为0开始消费
    
  2. 后续消费:
    第二次消费开始【消费者组已经在Kafka中存在】:根据上一次消费的每个分区Offset位置+1继续进行消费
    consumer offset消费者已经消费到这个分区的offset
    commit offset消费者下一个要消费的offset
    关系commit offset = consumer offset + 1

4.2 消费者如何知道消费位置

  1. 内存:
    • 每个消费者维护自己下一次要消费的commit offset放在自己的内存中。
    • 一旦消费者故障,内存数据会丢失,offset就丢失了
  2. 持久化处理:
    • Kafka默认方案:让每个消费者将自己负责的每个分区的commit offset存储在一个 __consumer_offsets 的topic中。
    • Spark/Flink默认方案:checkpoint,将自己每次消费成功以后的commit offset存储在HDFS文件中
  3. 自动提交:
    • 根据时间周期(每1s提交记录一次)来提交下一次要消费的offset,记录在__consumer_offsets中
    • 数据丢失的情况:如果刚消费,还没处理,就达到提交周期,记录了当前的offset。最后处理失败,需要重新消费处理,Kafka中已经记录消费过了,从上次消费的后面进行消费
    • 数据重复的情况:如果消费并处理成功,但是没有提交offset,程序故障重启,kafka中记录的还是之前的offset,重新又消一遍数据重复问题。
  4. 手动提交:
    • 根据处理的结果来实现手动提交,确认成功以后,再手动提交。

保证消费的不丢失(持久化处理)不重复(手动提交)精准的一次性语义

5. 负载均衡规则

5.1 生产者生产负载均衡规则

Kafka生产者生产数据的分区规则:

  1. 判断是否指定了某个分区,如果指定了分区,就写入指定的分区,如果没有执行2
  2. 判断是否自定义分区器【自定义分区规则】,如果有,就调用自定义分区器。如果没有就调用默认分区器
  3. 默认分区器:
    • 判断是否指定了Key,没有指定Key,就随机选择一个可用的分区
    • 如果指定了Key,就按照Key的MUR值取模分区个数,决定分区

5.2 消费者消费负载均衡规则

基本规则

  • 一个分区的数据只能由这个消费者组中的某一个消费者消费
  • 一个消费者可以消费多个分区的数据
  • 最优情况:消费者组的消费者个数等于分区个数

分配策略: 决定了多个分区如何分配给多个消费者
属性: partition.assignment.strategy =org.apache.kafka.clients.consumer.分配策略

  1. RangeAssignor:范围分配,默认的分配策略
    规则: 针对这个消费者组订阅的每个Topic进行分配,每个消费者消费一定的范围,如果不能均分,优先分配给编号小的.
    举例: 三个消费者,消费1个Topic,Topic1有8个分区

    消费者分区
    consumer 1T1【0,1,2】
    consumer 2T1【3,4,5】
    consumer 3T1【6,7】
  2. RoundRobinAssignor:轮询分配,常见于Kafka2.0之前的版本
    规则: 对整个消费者组订阅的所有Topic按照名称和顺序进行排序,然后轮询分配
    优点: 不论多个Topic能不能均分,都可以相对均衡的将分区分配每个消费者
    缺点: 如果某个消费者故障,无法恢复,轮询分配会重新对整体进行分配
    举例: 三个消费者,消费1个Topic,Topic1有8个分区

    消费者分区
    consumer 1T1【0,3,6】
    consumer 2T1【1,4,7】
    consumer 3T1【2,5】

    在这里插入图片描述

  3. StickyAssignor:黏性分配,2.0之后建议使用
    规则: 底层根据算法来进行平均分配,均衡度要比轮询更加均衡
    优点: 除了比轮询更加均衡以外,在出现故障转移的时候,每个分区保持不动,只增加多出来的分区
    举例: 三个消费者,消费1个Topic,Topic1有7个分区,后消费者3挂了
    consumer 3挂掉之前:

    消费者分区
    consumer 1T1【0,3,6】
    consumer 2T1【1,4】
    consumer 3T1【2,5】

    consumer 3挂掉之后:

    消费者分区
    consumer 1T1【0,3,6,5】
    consumer 2T1【1,4,2】

在这里插入图片描述

6. Kafka数据存储机制

6.1 写入过程

  1. 生产者生产每一条数据,将数据放入一个batch批次中,如果batch满了或者达到一定的时间,提交写入请求
  2. 生产者根据分区规则构建数据分区,获取对应的元数据,将请求提交给leader副本所在的Broker
  3. 先写入这台Broker的PageCache【页缓存】中,Kafka也用了内存机制来实现数据的快速的读写
    • Kafka使用OS内存:只有操作系统故障,重启机器,内存数据才会清空
  4. 操作系统的后台的自动将页缓存中的数据SYNC同步到磁盘文件中:最新的Segment的.log中
    • 占用操作内存达到10%或数据写入超过30s,顺序写磁盘,速度可以媲美写内存
  5. 其他的Follower到Leader中同步数据,Follower同步完成会返回ACK给Leader

6.2 Segment 设计

加快查询效率

  • 通过将分区的数据根据Offset划分多个比较小的Segment文件
  • 在检索数据时,可以根据Offset快速定位数据所在的Segment
  • 加载单个Segment文件查询数据,可以提高查询效率

减少删除数据IO

  • 删除数据时,Kafka以Segment为单位删除某个Segment的数据,避免一条一条删除,增加IO负载,性能较差

Segment的划分规则

  • 按照时间周期生成:

    #如果达到7天,重新生成一个新的Segment
    log.roll.hours = 168
    
  • 按照文件大小生成:

    #如果一个Segment存储达到1G,就构建一个新的Segment
    log.segment.bytes = 1073741824  
    

Segment文件的命名规则

  • 以当前文件存储的最小offset来命名的

6.3 读取过程

  1. 消费者根据Topic、Partition、Offset提交给Kafka请求读取数据
  2. Kafka根据元数据信息,找到对应的这个分区对应的Leader副本节点
  3. 请求Leader副本所在的Broker,先读PageCache(通过零拷贝机制【Zero Copy】读取PageCache)
  4. 如果PageCache中没有,读取Segment文件段,先根据offset找到要读取的那个Segment
  5. 将.log文件对应的.index文件加载到内存中,根据.index中索引的信息到Offset在.log文件中的最近物理位置
  6. 读取.log,根据索引找到对应Offset的数据,读取连续的一个批次的数据

6.4 kafka读写数据快的原因

写数据:

  • 先写入pagecache页缓存,操作系统的缓存
  • 顺序写

读数据:

  • 首先尝试读页缓存 如果没有读segment片段
  • 通过索引等确定消息位置之后 通过零拷贝技术读取数据。

6.5 数据清理

属性配置:

#开启清理
log.cleaner.enable = true
#清理规则
log.cleanup.policy = delete

基于存活时间规则:最常用的方式

# 清理周期
# 单位越小,优先级越高
log.retention.ms
log.retention.minutes
log.retention.hours=168/7# 检查周期,要搭配清理周期来修改
log.retention.check.interval.ms=300000
# Segment文件最后修改时间如果满足条件将被删除

基于文件大小规则

#删除文件阈值,如果整个数据文件大小,超过阈值的一个segment大小,将会删除最老的segment,-1表示不使用这种规则
log.retention.bytes = -1

7. Kafka消息队列的一次性语义

7.1 一次性语义

  1. at-most-once: 至多一次,允许为0次,可能数据丢失
  2. at-least-once: 至少一次,允许多次,可能数据重复
  3. exactly-once: 有且仅有一次,只有1次,精准数据传输一次性语义

7.2 Kafka如何保证生产一次性语义

  1. 保证生产数据不丢失:ACK + 重试机制

    ACK机制:acks = 0/1/all/-1

    1. 0:不等待ack,直接发送下一条
      优点:速度快 缺点:数据易丢失
    2. 1:生产者将数据写入Kafka,Kafka等待这个分区Leader副本,返回ack,发送下一条
      优点:性能和安全做了中和的选项。缺点:依旧存在一定概率的数据丢失的情况
    3. all/-1:生产者将数据写入Kafka,Kafka等待这个分区所有ISR【可用】副本同步成功,返回ack,发送下一条
      优点:安全。缺点:性能比较差

    重试机制: retries = 3 发送失败的重试次数

  2. 数据重复的情况 :ACK丢失
    幂等性机制:一个操作被执行多次,结果是一致的f(x) = f(f(x))在这里插入图片描述
    在每条数据中增加一个数据id,当前这一条数据会比上一条数据id多1,Kafka会根据id进行判断是否写入过了

    • 如果没有写入:写入kafka
    • 如果已经写入:直接返回ack

7.3 Kafka如何保证消费一次性语义

消费者是根据offset来持续消费,只要保证任何场景下消费者都能知道这个分区的commit Offset即可

commit offset每个消费者只保存在自己的内存中,如果消费者一旦故障,这个分区的commit offset就丢失。因此将每个分区的commit offset存储在一种可靠外部存储中,手动管理offset

  • step1:第一次消费根据属性进行消费
  • step2:消费分区数据,处理分区数据
  • step3:处理成功:将处理成功的分区的Offset进行额外的存储
  • step4:如果消费者故障,可以从外部存储读取上一次消费的offset向Kafka进行请求

8. Kafka分区副本概念

缩写全称解释
ARAll - Replicas(所有副本)指的是一个分区在所有节点上的副本(AR = ISR + OSR)
ISRIn - Sync - Replicas(可用副本)所有正在与Leader同步的Follower副本
OSROut - Sync - Replicas(不可用副本)长时间没有与Leader副本同步数据的follower副本
LWlow_watermark(最低消费的offset)一般为0,消费者能够消费到的最小的offset
HWhigh_watermark(最高消费的offset )当前这个分区所有副本同步的最低位置+1,消费者能消费到的最大位置
LEOLog End Offset (下一个待写的offset)当前已有的最新offset + 1
一个主题有3个分区,分区part0如下在分区有3个副本,数据如下
part0: Leader - node10-a, 1-b, 2-c, 3-d, 4-e, 5-f, 6-g, 7-h, 8-i
part0:Follower - node20-a, 1-b, 2-c, 3-d, 4-e, 5-f, 6-g
part0:Follower - node20-a, 1-b, 2-c, 3-d, 4-e, 5-f

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值