kafka 基础知识 第一讲

本文详细介绍了Kafka的基础知识,包括消息队列的两种模式、Kafka的特点、架构以及数据存储。Kafka是一个分布式消息中间件,支持高吞吐、低延迟、数据持久化和容错性。文章还涵盖了Kafka的命令行操作,如创建、查看和删除topic,以及生产者和消费者的使用。此外,讨论了生产者API,包括发送消息的方式、常见问题和分区策略。
摘要由CSDN通过智能技术生成

1.kafka 概述

kafka 是一个分布式的中间件,支持多分区、多副本、多订阅者,基于zookeeper协调的分布式消息系统。

1.1 消息队列的两种模式

1)点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
消息生产者生产消息发送到Queue中, 然后消息消费者从Queue中取出并且消费消息。

2)发布/订阅模式(一对多,消费者消费数据之后不会清除消息)
消息生产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。
点对点方式不同,发布到 topic 的消息会被所有订阅者消费。
在这里插入图片描述

3)消息队列的好处:
异步、削峰、解耦
在这里插入图片描述
在这里插入图片描述

1.2 kafka 特点

1.高吞吐量、低延迟
2.扩展性:kafka集群支持热扩展
3.持久性:支持数据持久化到磁盘
4.容错性:允许集群节点失败(n个副本,n-1节点存在)
5.高并发:多个客户端写入

1.3 kafka 架构

在这里插入图片描述
在这里插入图片描述
核心概念:
0.9 版本之前是依赖于zookeeper存储offset,0.9之后offset存储在特定的topic中
1)zookeeper:
(1) Kafka 通过 zookeeper 来存储集群的 meta 信息。
(2) 一旦controller所在broker宕机了,此时临时节点消失,集群里其他broker会一直监听这个临时节点,发现临时节点消失了,就争抢再次创建临时节点,保证有一台新的broker会成为controller角色。–选举controller
(3)存储consumer的offset信息。

2)producer:
消息生产者,发布消息到 Kafka集群的终端或服务。
3)broker:
Kafka集群中包含的服务器。
4)topic
每条发布到 Kafka集群的消息属于的类别,即 Kafka是面向 topic 的。可以理解为一个队列, 生产者和消费者面向的都是一个 topic;
5)partition
partition 是物理上的概念,每个 topic 包含一个或多个 partition。为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。
6)副本replica:
partition 的副本,保障 partition 的高可用。kafka 提供了副本机制,一个 topic 的每个分区都有若干个副本,一个 leader 和若干个follower。
7)leader:
replica 中的一个角色, producer 和 consumer 只跟 leader 交互。
leader partition 作用:
1.消费者读取数据,生成者写数据。都是在leader partition
2.维护了一个ISR,成为副本列表(a,b,c)先写到a副本里面,然后再写到b,c
三个副本列表消息写完说明这个消息真正的写完了。如果c有问题,说明整个写入是失败了。如果c写入不成功会被我们leader partition给删除掉。(a,b)。
8)follower
replica 中的一个角色,从 leader 中复制数据。定期的从leader partition上同步数据。保持和 leader 数据的同步。 leader 发生故障时,某个 follower 会成为新的 leader。(心跳机制)
9)偏移量offset
每条数据都有一个offset,是数据在该partition的中的唯一标识。每个consumer会保存其消费到的offset的位置。消费偏移量专门的topic的__consumer_offset中。0.10x保存在zk中

1.4 数据存储

在这里插入图片描述
consumer_offsets也是topic,各个消费者的偏移量记录
test-0/test-1 test是topic名称,test-0/1/2/3/4是test的这个topic的那个分区目录。
在这里插入图片描述
由于生产者生产的消息不断追加log末尾,防止过大影响性能,采用分片和索引机制。将每个partition分为多个segment,每个segment对应两个文件。一个是log和.index文件。该文件夹命名是topic名称-分区序号
在这里插入图片描述
其中.index和log名字是当前segment的第一条消息的offset命名。index索引指向的是message的物理偏移信息。

2.命令行操作

2.1 创建topic

bin/kafka-topics.sh --zookeeper dev0:2181,dev1:2181,dev2:2181 --create  --replication-factor 3 --partitions 3 --topic test2

–replication-factor 副本数量
–partitions 分区数量
–topic topic名称

2.2 查看当前系统的中所有topic

bin/kafka-topics.sh --zookeeper dev0:2181,dev1:2181,dev2:2181 --list

2.3 删除topic

bin/kafka-topics.sh --delete --topic test2 --zookeeper dev0:2181,dev1:2181,dev2:2181 

delete.topic.enable=true

2.4 查看某个topic的详情

bin/kafka-topics.sh --zookeeper dev0:2181,dev1:2181,dev2:2181 --describe --topic test

在这里插入图片描述
ISR:in sync relicas 同步副本列表
leader:95 leader partition是机器数,replicas:副本所在机器broker

2.5 修改分区数

bin/kafka-topics.sh --zookeeper dev0:2181,dev1:2181,dev2:2181 --alter --topic test --partitions 5

kafka 只支持增加分区,不支持减少分区
原因:减少分区,代价太大

2.6 topic动态更新

bin/kafka-topics.sh --zookeeper dev0:2181,dev1:2181,dev2:2181 --entity-type topics 

2.7 生产者producer

bin/kafka-console-producer.sh --broker-list dev0:9092 --topic test

2.8 消费者

 bin/kafka-console-consumer.sh --bootstrap-server dev0:9092 --topic test

想消费之前的数据–从头消费

bin/kafka-console-consumer.sh --bootstrap-server dev0:9092 --topic test --from-beginning

指定要消费的分区,和要消费的起始offset

 bin/kafka-console-consumer.sh --bootstrap-server dev0:9092 --topic test --offset 2 --partition 0

3.生产者api

public class ProducerDemo {
    public static void main(String[] args) {
        Properties props = new Properties();
        //设置kafka集群的地址
        props.put("bootstrap.servers",
                "dev0:9092,dev1:9092,dev2:9092");
        //设置ack模式,all是最慢的但是最安全
        props.put("acks","all");
        //失败重试次数
        props.put("retries",0);
        //数据发送批次大小
        props.put("batch.size",10);
        //数据序列化和反序列化
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());

        //设置幂等性--是否开启
    props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG,"false");
       
        /**
         * 构造生产者实例对象
         */
        // 创建一个Producer实例:线程资源,跟各个broker建立socket连接资源
        KafkaProducer<String, String> producer = new KafkaProducer<>(props);

        /**
         *  构造消息发送
         * 如果发送消息,消息不指定key,那么我们发送的这些消息,会被轮训的发送到不同的分区。
         * 如果指定了key。发送消息的时候,客户端会根据这个key计算出来一个hash值,
         * 根据这个hash值会把消息发送到对应的分区里面。
         */
        for(int i=0;i<2;i++) {
            ProducerRecord<String, String> msg = new ProducerRecord<>("test", "name"+i, "this is test");
            producer.send(msg);
        }
     producer.close();
    }
}

3.1 kafka 发送消息两种方式

1.同步发送
//第二种方式:这是同步发送的模式
producer.send(record).get();
// 你要一直等待人家后续一系列的步骤都做完,发送消息之后
// 有了消息的回应返回给你,你这个方法才会退出来

       //第二种方式:这是同步发送的模式
		producer.send(record).get(); 
        // 你要一直等待人家后续一系列的步骤都做完,发送消息之后
        // 有了消息的回应返回给你,你这个方法才会退出来。

2.异步发送

--异步发送
//kafka发送数据有两种方式:
        //1:异步的方式。
 //异步发送不管是否响应,一直发,集体接收
for(int i=0;i<2;i++) {
            ProducerRecord<String, String> msg = new ProducerRecord<>("test", "name"+i, "this is test");
            producer.send(msg,new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    if (exception == null) {
                        // 消息发送成功
                        System.out.println("消息发送成功");
                    } else {
                        // 消息发送失败,需要重新发送
                    }
                }
            });
            
        }

3.2整体代码实现

public class ProducerDemo {
    public static void main(String[] args) throws Exception {

        Properties props = new Properties();
        //设置kafka集群的地址
        props.put("bootstrap.servers",
                "dev0:9092,dev1:9092,dev2:9092");
        //设置ack模式,all是最慢的但是最安全
        props.put("acks","all");
        //失败重试次数
        props.put("retries",0);
        //数据发送批次大小
        props.put("batch.size",10);
        //数据序列化和反序列化
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());

        //设置幂等性--是否开启
        props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG,"false");
        //


        /**
         * 构造生产者实例对象
         */
        // 创建一个Producer实例:线程资源,跟各个broker建立socket连接资源
        KafkaProducer<String, String> producer = new KafkaProducer<>(props);

        /**
         *  构造消息发送
         * 如果发送消息,消息不指定key,那么我们发送的这些消息,会被轮训的发送到不同的分区。
         * 如果指定了key。发送消息的时候,客户端会根据这个key计算出来一个hash值,
         * 根据这个hash值会把消息发送到对应的分区里面。
         */
        for(int i=0;i<2;i++) {
            ProducerRecord<String, String> msg = new ProducerRecord<>("test", "name"+i, "this is test");
            producer.send(msg,new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    if (exception == null) {
                        // 消息发送成功
                        System.out.println("消息发送成功");
                    } else {
                        // 消息发送失败,需要重新发送
                    }
                }
            });

        }


        ProducerRecord<String, String> record = new ProducerRecord<>("test", "name", " testlong");

        //第二种方式:这是同步发送的模式
		producer.send(record).get();
        // 你要一直等待人家后续一系列的步骤都做完,发送消息之后
        // 有了消息的回应返回给你,你这个方法才会退出来
        producer.close();
    }
}

3.3 producer发送数据原理

Producer 发送消息原理:
broker启动起来的时候都会把元数据在zk上注册。会去竞争谁是主节点。主节点是controller。谁先注册成功谁就拿到controller这个角色。controller 会监听zk的目录。监听元数据变化。并可以从zk同步到controller。会把同步的元数据分发给其他的broker。
生产者,生产者会把数据封装成一个对象,ProducerRecord对象,里面包含了要发送的topic,然后这个对象交给序列化器。将对象转换成二进制进行传输。
具体在哪个分区,分区key,消息内容,timestamp时间戳,分区器要往一个主题或者topic发送消息。发送到那个分区里面。leader partition。(获取元数据)leadpartition 是要承接读写的任务。followerpartition定期的leader partitions去拉取数据。
缓冲区:send 线程,从缓冲区里获取数据。多条数据封装成一个batch.通过这个线程把数据发送到leader partitions 的broker,写到os cache 缓存里面。达到时间或者空间条件,刷写到磁盘里面。
在这里插入图片描述

3.4 几个常见的生产者的问题:

1.生产者如何提高吞吐量
1.1.buffer.memory:设置发送消息的缓冲区,默认值是33554432,就是32MB。写数据变慢。如果发送消息出去的速度小于写入消息进去的速度,就会导致缓冲区写满。(调大)
1.2 compression.type,默认是none,不压缩,但是也可以使用lz4压缩,效率还是不错的。减小数据量,提升吞吐量,默认值是:16384,就是16kb。
1.3 batch.size,设置每个batch的大小,如果batch太小,会导致频繁网络请求,吞吐量下降。
1.4 linger.ms,这个值默认是0,意思就是消息必须立即被发送。一般设置一个100毫秒之类的,满了100ms 或者是满了16kb的时候就会发送出去。
1.3和1.4结合使用

3.5 分区器-分区

1.没有设置key的时候:
我们的消息就会被轮询的发送到不同的分区。
2.设置了key
kafka 自带的分区器,会根据key计算出一个hash值。这个哈希值对应某一个分区。如果key相同。那么hash值必然相同,key相同的值。必然会分到同一个分区
3.自定义分区。
props.put(“potitioner.class”,“com.zhss.HotDataPartitioner”);

3.6 生产者在数据发送过程中常见的异常

1.1 LeaderNotAvailableException:这个就是如果某台机器挂了,此时leader副本不可用,会导致你写入失败,要等待其他follower副本切换为leader副本之后,才能继续写入,此时可以重试发送即可。如果
说你平时重启kafka的broker进程,肯定会导致leader切换,一定会导致你写入报错,是LeaderNotAvailableException。
1.2 NotControllerException:这个也是同理,如果说Controller所在Broker挂了,那么此时会有问题,需要等待Controller重新选举,此时也是一样就是重试即可
3)NetworkException:网络异常,重试即可
我们之前配置了一个参数,retries,他会自动重试的,但是如果重试几次之后还是不行,就会提供Exception给我们来处理了。
重试机制:
参数:retries 默认值是3
参数:retry.backoff.ms 两次重试之间的时间间隔。

3.7 生产者发送消息重试带来问题

1.消息会重复
leader 切换需要重试,或者是网络延迟导致数据重复发送。但是已经成功了。
2.消息乱序:
重试导致消息的乱序。1,2,3 2,3,1

可以设置参数:
props.put(“max.in.flight.requests.per.connection”,1);
设置保证producer同一时间只能发送一条消息。
一个成功再写入写入另外一个。

3.8 ack判断是否发送成功

1.ack 参数详解:
request.required.acks=0;
只要请求发送出去,就算发送完了,不关心是否写成功。性能很好,可承受丢失
2.request.required.acks=1;
发送一条消息,当leader partition 写入成功之后,才算成功。
这种方式可能丢数据。
3.request.required.acked.acks=-1;
需要ISR(副本)列表里面,所有的副本写完之后,这条消息才算写成功。ISR:3 个副本,1leader partition 2 follow partition

kafka 服务端相关参数:
min.insync.replicas:1 如果不设置,默认是1
设置是2 如果我们这个ISR里面最少有两个副本。如果只有一个副本的话
往这个分区插入数据的时候会出错。

3.9 设计不丢数据方案

1.首先创建主题的时候,分区副本要大于等于2
2.request.required.acks=-1;
3.min.insync.replicas>=2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据学习爱好者

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值