当前位置: 首页 > ops >正文

KafKa学习笔记

(1)什么是Kafka

①什么是消息队列
在这里插入图片描述

消息队列中间件就是用来存储消息的软件(组件)。举个例子来理解,为了分析网站的用户行为,我们需要记录用户的访问日志。这些一条条的日志,可以看成是一条条的消息,我们可以将它们保存到消息队列中。将来有一些应用程序需要处理这些日志,就可以随时将这些消息取出来处理。目前市面上的消息队列有很多,例如:Kafka、RabbitMQ、ActiveMQ、RocketMQ、ZeroMQ等。以下是两种模式:
在这里插入图片描述

以下是KafKa的应用场景:
在这里插入图片描述

(2)Kafka的一些核心概念

①broker

在这里插入图片描述

②zookeeper(KafKa正在去Zookeeper)

在这里插入图片描述
(1)ZK用来管理和协调broker,存储了Kafka的元数据(例如:有多少topic、partition、consumer)
(2)ZK主要用于通知生产者和消费者Kafka集群中有新的broker加入、或者Kafka集群中出现故障的broker。

③生产者和消费者

producer(生产者)生产者负责将数据推送给broker的topic
consumer(消费者) 消费者负责从broker的topic中拉取数据,并自己进行处理
在这里插入图片描述

④主题和分区

Topic(主题)是Kafka中最核心的概念之一,可以理解为消息的类别或分类
在这里插入图片描述

1 分区(Partitions):每个Topic可以被分成多个分区,这是Kafka实现横向扩展和高并发的关键
2 消息顺序保证; 在单个分区内:消息是有序的(先进先出),跨分区的话消息顺序不保证
3 持久化存储:消息会持久化到磁盘,即使消费者离线,回来还能继续消费。
在这里插入图片描述

⑤副本

在这里插入图片描述

⑥Offset偏移量

在这里插入图片描述

⑦Kafka生产者幂等性与事务

(1)Kafka幂等性:生产者发送消息时,即使重复发送,Broker也只会存储一次。
在这里插入图片描述
(2)Kafka事务:2017年Kafka 0.11.0.0引入的新特性。类似于数据库的事务。Kafka事务指的是生产者生产消息以及消费者提交offset的操作可以在一个原子操作中,要么都成功,要么都失败。

在这里插入图片描述

(3)Kafka的一些命令

#!/bin/bash
# Kafka常用命令大全
# 文件名: kafka_commands.sh# 1. 创建Topic
bin/kafka-topics.sh --create --bootstrap-server 192.168.10.102:9092 --partitions 3 --replication-factor 1 --topic my-topic# 2. 查看所有Topic
bin/kafka-topics.sh --list --bootstrap-server 192.168.10.102:9092# 3. 查看Topic详情
bin/kafka-topics.sh --describe --bootstrap-server 192.168.10.102:9092 --topic my-topic# 4. 删除Topic
bin/kafka-topics.sh --delete --bootstrap-server 192.168.10.102:9092 --topic my-topic# 5. 修改Topic分区数
bin/kafka-topics.sh --alter --bootstrap-server 192.168.10.102:9092 --partitions 5 --topic my-topic# 6. 启动控制台生产者
bin/kafka-console-producer.sh --broker-list 192.168.10.102:9092 --topic my-topic# 7. 启动控制台消费者(从开始消费)
bin/kafka-console-consumer.sh --bootstrap-server 192.168.10.102:9092 --topic my-topic --from-beginning# 8. 消费者(只消费新消息)
bin/kafka-console-consumer.sh --bootstrap-server 192.168.10.102:9092 --topic my-topic# 9. 消费者(带key显示)
bin/kafka-console-consumer.sh --bootstrap-server 192.168.10.102:9092 --topic my-topic --property print.key=true --property key.separator=" : " --from-beginning# 10. 查看所有消费者组
bin/kafka-consumer-groups.sh --bootstrap-server 192.168.10.102:9092 --list# 11. 查看消费者组详情
bin/kafka-consumer-groups.sh --bootstrap-server 192.168.10.102:9092 --describe --group my-consumer-group# 12. 重置消费者偏移量(到最早)
bin/kafka-consumer-groups.sh --bootstrap-server 192.168.10.102:9092 --group my-consumer-group --reset-offsets --to-earliest --execute --topic my-topic# 13. 重置到最新位置
bin/kafka-consumer-groups.sh --bootstrap-server 192.168.10.102:9092 --group my-consumer-group --reset-offsets --to-latest --execute --topic my-topic# 14. 查看Topic消息数量
bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list 192.168.10.102:9092 --topic my-topic --time -1# 15. 查看Broker配置
bin/kafka-configs.sh --bootstrap-server 192.168.10.102:9092 --entity-type brokers --entity-name 0 --describe# 16. 查看Topic配置
bin/kafka-configs.sh --bootstrap-server 192.168.10.102:9092 --entity-type topics --entity-name my-topic --describe# 17. 查看集群ID
bin/kafka-cluster.sh --bootstrap-server 192.168.10.102:9092 --cluster-id# 18. 查看Broker信息
bin/zookeeper-shell.sh localhost:2181 ls /brokers/ids# 19. 查看Topic在Zookeeper中的信息
bin/zookeeper-shell.sh localhost:2181 ls /brokers/topics# 20. 生产者性能测试
bin/kafka-producer-perf-test.sh --topic test-perf --num-records 1000000 --record-size 1000 --throughput -1 --producer-props bootstrap.servers=192.168.10.102:9092# 21. 消费者性能测试
bin/kafka-consumer-perf-test.sh --topic test-perf --bootstrap-server 192.168.10.102:9092 --messages 1000000

(4)Kafka Java API开发

生产者程序开发

  1. 创建连接
    • bootstrap.servers:Kafka的服务器地址
    • acks:表示当生产者生产数据到Kafka中,Kafka中会以什么样的策略返回
    • key.serializer:Kafka中的消息是以key、value键值对存储的,而且生产者生产的消息是需要在网络上传到的,这里指定的是StringSerializer方式,就是以字符串方式发送(将来还可以使用其他的一些序列化框架:Google ProtoBuf、Avro)
    • value.serializer:同上
  2. 创建一个生产者对象KafkaProducer
  3. 调用send方法发送消息(ProducerRecor,封装是key-value键值对)
  4. 调用Future.get表示等带服务端的响应
  5. 关闭生产者
public class KafkaProducerTest {public static void main(String[] args) throws ExecutionException, InterruptedException {// 1. 创建用于连接Kafka的Properties配置Properties props = new Properties();props.put("bootstrap.servers", "node1.itcast.cn:9092");props.put("acks", "all");props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");// 2. 创建一个生产者对象KafkaProducerKafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(props);// 3. 发送1-100的消息到指定的topic中for(int i = 0; i < 100; ++i) {// 构建一条消息,直接new ProducerRecordProducerRecord<String, String> producerRecord = new ProducerRecord<>("test", null, i + "");Future<RecordMetadata> future = kafkaProducer.send(producerRecord);// 调用Future的get方法等待响应future.get();System.out.println("第" + i + "条消息写入成功!");}// 4.关闭生产者kafkaProducer.close();}
}

PS:代码类的详解

(1)KafkaProducer:KafkaProducerKafkaJava 客户端生产者类,用于向 Kafka 集群发送消息
KafkaProducer<String, String> 中的两个 String 分别代表:Key的类型,Value的类型Key:决定消息发送到哪个分区,相同Key的消息会被发送到同一分区,保证顺序性便于按Key进行数据处理和聚合Value:实际消息内容:要传递的业务数据(2)ProducerRecordKafka 生产者用来封装要发送的消息的核心类,它包含了消息的所有元数据和内容。public class ProducerRecord<K, V> {private final String topic;      // 目标Topicprivate final Integer partition; // 指定分区(可选)private final K key;            // 消息Keyprivate final V value;          // 消息Valueprivate final Long timestamp;   // 时间戳(可选)private final Iterable<Header> headers; // 消息头(可选)
}以下是最基本的创建ProducerRecord的方法:
// Key用于决定消息去往哪个分区
ProducerRecord<String, String> record1 = new ProducerRecord<>("orders",                   // topic"user-123",                 // Key: 用户ID ← 第一个String"订单创建: order-001"        // Value: 消息内容
);ProducerRecord<String, String> record2 = new ProducerRecord<>("orders",                   // topic"user-456",                 // 不同Key,可能去往不同分区"订单创建: order-002"        // Value: 消息内容
);
再接下来是一个经典的应用场景日志收集:
// Key: 服务器名称, Value: 日志内容
ProducerRecord<String, String> record = new ProducerRecord<>("app-logs","web-server-01",  // ← 按服务器分区"ERROR: Database connection failed"
);(3)FutureJava 并发编程中一个极其重要的接口,它代表一个异步计算的结果
(4)RecordMetadataKafka 生产者发送消息后返回的元数据信息,它包含了消息在 Kafka 集群中的存储详情。

消费者程序开发

  • group.id:消费者组的概念,可以在一个消费组中包含多个消费者。如果若干个消费者的group.id是一样的,表示它们就在一个组中,一个组中的消费者是共同消费Kafka中topic的数据。
  • Kafka是一种拉消息模式的消息队列,在消费者中会有一个offset,表示从哪条消息开始拉取数据
  • kafkaConsumer.poll:Kafka的消费者API是一批一批数据的拉取
/*** 消费者程序** 1.创建Kafka消费者配置* Properties props = new Properties();* props.setProperty("bootstrap.servers", "node1.itcast.cn:9092");* props.setProperty("group.id", "test");* props.setProperty("enable.auto.commit", "true");* props.setProperty("auto.commit.interval.ms", "1000");* props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");* props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");** 2.创建Kafka消费者* 3.订阅要消费的主题* 4.使用一个while循环,不断从Kafka的topic中拉取消息* 5.将将记录(record)的offset、key、value都打印出来*/
public class KafkaConsumerTest {public static void main(String[] args) {// 1.创建Kafka消费者配置Properties props = new Properties();props.setProperty("bootstrap.servers", "node1.itcast.cn:9092");// 消费者组(可以使用消费者组将若干个消费者组织到一起),共同消费Kafka中topic的数据// 每一个消费者需要指定一个消费者组,如果消费者的组名是一样的,表示这几个消费者是一个组中的props.setProperty("group.id", "test");// 自动提交offsetprops.setProperty("enable.auto.commit", "true");// 自动提交offset的时间间隔props.setProperty("auto.commit.interval.ms", "1000");// 拉取的key、value数据的props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");// 2.创建Kafka消费者KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(props);// 3. 订阅要消费的主题// 指定消费者从哪个topic中拉取数据kafkaConsumer.subscribe(Arrays.asList("test"));// 4.使用一个while循环,不断从Kafka的topic中拉取消息while(true) {// Kafka的消费者一次拉取一批的数据ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(5));// 5.将将记录(record)的offset、key、value都打印出来for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {// 主题String topic = consumerRecord.topic();// offset:这条消息处于Kafka分区中的哪个位置long offset = consumerRecord.offset();// key\valueString key = consumerRecord.key();String value = consumerRecord.value();System.out.println("topic: " + topic + " offset:" + offset + " key:" + key + " value:" + value);}}}
}

消费者的那些类可以类比生产者

生产者使用异步方式生产消息

  • 使用匿名内部类实现Callback接口,该接口中表示Kafka服务器响应给客户端,会自动调用onCompletion方法
    • metadata:消息的元数据(属于哪个topic、属于哪个partition、对应的offset是什么)
    • exception:这个对象Kafka生产消息封装了出现的异常,如果为null,表示发送成功,如果不为null,表示出现异常。
// 二、使用异步回调的方式发送消息
ProducerRecord<String, String> producerRecord = new ProducerRecord<>("test", null, i + "");
kafkaProducer.send(producerRecord, new Callback() {@Overridepublic void onCompletion(RecordMetadata metadata, Exception exception) {// 1. 判断发送消息是否成功if(exception == null) {// 发送成功// 主题String topic = metadata.topic();// 分区idint partition = metadata.partition();// 偏移量long offset = metadata.offset();System.out.println("topic:" + topic + " 分区id:" + partition + " 偏移量:" + offset);}else {// 发送出现错误System.out.println("生产消息出现异常!");// 打印异常消息System.out.println(exception.getMessage());// 打印调用栈System.out.println(exception.getStackTrace());}}
});

Kafka中的分区副本机制

生产者的分区写入策略

  • 轮询(按照消息尽量保证每个分区的负载)策略,消息会均匀地分布到每个partition
    • 写入消息的时候,key为null的时候,默认使用的是轮询策略
  • 随机策略(不使用)
  • 按key写入策略,key.hash() % 分区的数量
  • 自定义分区策略(类似于MapReduce指定分区)

乱序问题

  • 在Kafka中生产者是有写入策略,如果topic有多个分区,就会将数据分散在不同的partition中存储
  • 当partition数量大于1的时候,数据(消息)会打散分布在不同的partition中
  • 如果只有一个分区,消息是有序的

消费组Consumer Group Rebalance机制

  • 再均衡:在某些情况下,消费者组中的消费者消费的分区会产生变化,会导致消费者分配不均匀(例如:有两个消费者消费3个,因为某个partition崩溃了,还有一个消费者当前没有分区要削峰),Kafka Consumer Group就会启用rebalance机制,重新平衡这个Consumer Group内的消费者消费的分区分配。
  • 触发时机
    • 消费者数量发生变化
      • 某个消费者crash
      • 新增消费者
    • topic的数量发生变化
      • 某个topic被删除
    • partition的数量发生变化
      • 删除partition
      • 新增partition
  • 不良影响
    • 发生rebalance,所有的consumer将不再工作,共同来参与再均衡,直到每个消费者都已经被成功分配所需要消费的分区为止(rebalance结束)

消费者的分区分配策略

分区分配策略:保障每个消费者尽量能够均衡地消费分区的数据,不能出现某个消费者消费分区的数量特别多,某个消费者消费的分区特别少

  • Range分配策略(范围分配策略):Kafka默认的分配策略
    • n:分区的数量 / 消费者数量
    • m:分区的数量 % 消费者数量
    • 前m个消费者消费n+1个分区
    • 剩余的消费者消费n个分区
  • RoundRobin分配策略(轮询分配策略)
    • 消费者挨个分配消费的分区
  • Striky粘性分配策略
    • 在没有发生rebalance跟轮询分配策略是一致的
    • 发生了rebalance,轮询分配策略,重新走一遍轮询分配的过程。而粘性会保证跟上一次的尽量一致,只是将新的需要分配的分区,均匀的分配到现有可用的消费者中即可
    • 减少上下文的切换

副本的ACK机制

producer是不断地往Kafka中写入数据,写入数据会有一个返回结果,表示是否写入成功。这里对应有一个ACKs的配置。

  • acks = 0:生产者只管写入,不管是否写入成功,可能会数据丢失。性能是最好的
  • acks = 1:生产者会等到leader分区写入成功后,返回成功,接着发送下一条
  • acks = -1/all:确保消息写入到leader分区、还确保消息写入到对应副本都成功后,接着发送下一条,性能是最差的

根据业务情况来选择ack机制,是要求性能最高,一部分数据丢失影响不大,可以选择0/1。如果要求数据一定不能丢失,就得配置为-1/all。

分区中是有leader和follower的概念,为了确保消费者消费的数据是一致的,只能从分区leader去读写消息,follower做的事情就是同步数据,Backup。

高级API(High-Level API)、低级API(Low-Level API)

  • 高级API就是直接让Kafka帮助管理、处理分配、数据
    • offset存储在ZK中
    • 由kafka的rebalance来控制消费者分配的分区
    • 开发起来比较简单,无需开发者关注底层细节
    • 无法做到细粒度的控制
  • 低级API:由编写的程序自己控制逻辑
    • 自己来管理Offset,可以将offset存储在ZK、MySQL、Redis、HBase、Flink的状态存储
    • 指定消费者拉取某个分区的数据
    • 可以做到细粒度的控制
    • 原有的Kafka的策略会失效,需要我们自己来实现消费机制

Kafka原理

leader和follower

  • Kafka中的leader和follower是相对分区有意义,不是相对broker
  • Kafka在创建topic的时候,会尽量分配分区的leader在不同的broker中,其实就是负载均衡
  • leader职责:读写数据
  • follower职责:同步数据、参与选举(leader crash之后,会选举一个follower重新成为分区的leader
  • 注意和ZooKeeper区分
    • ZK的leader负责读、写,follower可以读取
    • Kafka的leader负责读写、follower不能读写数据(确保每个消费者消费的数据是一致的),Kafka一个topic有多个分区leader,一样可以实现数据操作的负载均衡

AR\ISR\OSR

  • AR表示一个topic下的所有副本
  • ISR:In Sync Replicas,正在同步的副本(可以理解为当前有几个follower是存活的)
  • OSR:Out of Sync Replicas,不再同步的副本
  • AR = ISR + OSR

leader选举

  • Controller:controller是kafka集群的老大,是针对Broker的一个角色

    • Controller是高可用的,是用过ZK来进行选举
  • Leader:是针对partition的一个角色

    • Leader是通过ISR来进行快速选举
  • 如果Kafka是基于ZK来进行选举,ZK的压力可能会比较大。例如:某个节点崩溃,这个节点上不仅仅只有一个leader,是有不少的leader需要选举。通过ISR快速进行选举。

  • leader的负载均衡

    • 如果某个broker crash之后,就可能会导致partition的leader分布不均匀,就是一个broker上存在一个topic下不同partition的leader
    • 通过以下指令,可以将leader分配到优先的leader对应的broker,确保leader是均匀分配的
    bin/kafka-leader-election.sh --bootstrap-server node1.itcast.cn:9092 --topic test --partition=2 --election-type preferred
    

Kafka读写流程

  • 写流程
    • 通过ZooKeeper找partition对应的leader,leader是负责写的
    • producer开始写入数据
    • ISR里面的follower开始同步数据,并返回给leader ACK
    • 返回给producer ACK
  • 读流程
    • 通过ZooKeeper找partition对应的leader,leader是负责读的
    • 通过ZooKeeper找到消费者对应的offset
    • 然后开始从offset往后顺序拉取数据
    • 提交offset(自动提交——每隔多少秒提交一次offset、手动提交——放入到事务中提交)

Kafka的物理存储

  • Kafka的数据组织结构
    • topic
    • partition
    • segment
      • .log数据文件
      • .index(稀疏索引)
      • .timeindex(根据时间做的索引)
  • 深入了解读数据的流程
    • 消费者的offset是一个针对partition全局offset
    • 可以根据这个offset找到segment段
    • 接着需要将全局的offset转换成segment的局部offset
    • 根据局部的offset,就可以从(.index稀疏索引)找到对应的数据位置
    • 开始顺序读取

消息传递的语义性

Flink里面有对应的每种不同机制的保证,提供Exactly-Once保障(二阶段事务提交方式)

  • At-most once:最多一次(只管把数据消费到,不管有没有成功,可能会有数据丢失)
  • At-least once:最少一次(有可能会出现重复消费)
  • Exactly-Once:仅有一次(事务性性的保障,保证消息有且仅被处理一次)

Kafka的消息不丢失

  • broker消息不丢失:因为有副本relicas的存在,会不断地从leader中同步副本,所以,一个broker crash,不会导致数据丢失,除非是只有一个副本。
  • 生产者消息不丢失:ACK机制(配置成ALL/-1)、配置0或者1有可能会存在丢失
  • 消费者消费不丢失:重点控制offset
    • At-least once:一种数据可能会重复消费
    • Exactly-Once:仅被一次消费

数据积压

  • 数据积压指的是消费者因为有一些外部的IO、一些比较耗时的操作(Full GC——Stop the world),就会造成消息在partition中一直存在得不到消费,就会产生数据积压
  • 在企业中,我们要有监控系统,如果出现这种情况,需要尽快处理。虽然后续的Spark Streaming/Flink可以实现背压机制,但是数据累积太多一定对实时系统它的实时性是有说影响的

数据清理&配额限速

  • 数据清理
    • Log Deletion(日志删除):如果消息达到一定的条件(时间、日志大小、offset大小),Kafka就会自动将日志设置为待删除(segment端的后缀名会以 .delete结尾),日志管理程序会定期清理这些日志
      • 默认是7天过期
    • Log Compaction(日志合并)
      • 如果在一些key-value数据中,一个key可以对应多个不同版本的value
      • 经过日志合并,就会只保留最新的一个版本
  • 配额限速
    • 可以限制Producer、Consumer的速率
    • 防止Kafka的速度过快,占用整个服务器(broker)的所有IO资源
http://www.xdnf.cn/news/18935.html

相关文章:

  • 【Day 35】Linux-Mysql错误总结
  • DA14531(Cortex-M0+)之Wake-up Interrupt Controller (WIC)
  • React学习教程,从入门到精通, ReactJS - 安装:初学者指南(3)
  • linux 网络:并发服务器及IO多路复用
  • 如何将yolo训练图像数据库的某个分类的图像取出来
  • element-plus的el-scrollbar显示横向滚动条
  • 使用华为 USG6000防火墙配置安全策略
  • 传输层协议介绍
  • 企业通讯软件以安全为基,搭建高效的通讯办公平台
  • Python篇---返回类型
  • 【论文阅读】PEPNet
  • amis上传组件导入文件接口参数为base64格式的使用示例
  • 计算机三级嵌入式填空题——真题库(22)原题附答案速记
  • 强化学习与注意力机制的AlignSAM框架解析
  • 微算法科技(NASDAQ:MLGO)推出创新型混合区块链共识算法,助力物联网多接入边缘计算
  • [n8n] 工作流数据库管理SQLite | 数据访问层-REST API服务
  • Paimon——官网阅读:Flink 引擎
  • 前端javascript在线生成excel,word模板-通用场景(免费)
  • AbMole小课堂丨详解野百合碱在动物肺动脉高压、急性肺损伤、静脉闭塞肝病造模中的原理及应用
  • Go 语言常用命令使用与总结
  • 微信小程序对接EdgeX Foundry详细指南
  • 云计算学习100天-第31天
  • 从零开始的云计算生活——第五十三天,发愤图强,kubernetes模块之Prometheus和发布
  • 【SpringAI】快速上手,详解项目快速集成主流大模型DeepSeek,ChatGPT
  • 【TEC045-KIT】基于复旦微 FMQL45T900 的全国产化 ARM 开发套件
  • Uniapp中自定义导航栏
  • 如何将iPhone上的隐藏照片传输到电脑
  • Flask测试平台开发实战-第二篇
  • 服务器核心组件:CPU 与 GPU 的核心区别、应用场景、协同工作
  • 麒麟操作系统挂载NAS服务器