Kafka相关原理、API和面试题总结

1.kafka工作流程图

2.kafka文件存储机制

由于生产者生产的消息会不断追加到log文件末尾,为防止log文件过大导致数据定位效率低下,Kafka采取了分片和索引机制,将每个partition分为多个segment。每个segment对应两个文件——“.in dex”文件和“.log”文件。这些文件位于一个文件夹下,该文件夹的命名规则为:topic名称+分区序号。例如,first这个topic有三个分区,则其对应的文件夹为first-0,first-1,first-2。

00000000000000000000.index

00000000000000000000.log

00000000000000170410.index

00000000000000170410.log

00000000000000239430.index

00000000000000239430.log

 

å¨è¿éæå¥å¾çæè¿°

index和log文件以当前segment的第一条消息的offset命名。下图为index文件和log文件的结构示意图。

3.Kafka的数据可靠性

为保证producer发送的数据,能够可靠的发送到topic,topic在收到数据后,需向producer发送ack,producer收到ack后进行下一轮发送,否则进行重发。

发送时机:确保follower与leader同步完成,leader再发送ack,才能保证leader挂掉之后,能在剩下的follower中选举新的leader。

多少个follower同步完成后发送ack?

① 半数以上follower同步完成。②全部follower同步完成。

两种情况比较:

副本数据同步策略

方案

优点

缺点

半数以上完成同步,就发送ack

延迟低

选举新的leader时,容忍n台节点的故障,需要2n+1个副本

全部完成同步,才发送ack

选举新的leader时,容忍n台节点的故障,需要n+1个副本

延迟高

kafka选用第二种方案,减少数据冗余,延迟影响很小。

4.ISR和acks参数

ISR(In-sync replicas):同步副本队列。

follower因某种原因或故障迟迟不能与leader同步,leader会将此follower暂时踢出ISR。这个超时时间有参数:replica.lag.time.max.ms 决定。

acks参数:① 为0 时,不等待直接发ack ② 为1时,只等leader写完数据就发ack ③ 为-1时 等ISR全部同步完数据后再发ISR

前两种情况有可能丢数据,第三种情况可能会数据重复。

5.数据丢失和数据重复的案例

1.数据丢失案例:acks=1

①producer给leader发数据,不等follower写入磁盘就返回ack

② 此时没同步,但是leader挂掉了,会选举出新的leader,由于已经发送过ack,之前发送的Hello认为消费完成,不会再发送,会继续发送下一个消息“Kafka”,而此时的新leader还没有Hello这个数据,Hello就丢掉了。

2.数据重复案例:acks=-1

①producer给leader发送数据后,等follower写入磁盘后,准备返回ack。

在还没返回ack时,恰巧此时leader挂了,ack没发回去,producer会认为Hello这个消息没发送成功,但其实已经写入磁盘了。选取新的Leader后,会重新发Hello这个消息。所以重复。

6.数据一致性的保证

leader出现故障后,出现新的leader,为保证副本之间的数据一致,follower会先将各自log文件高于HW的部分截掉,再从新leader同步数据。

7.Kafka为什么读写数据高效?

① 顺序写磁盘,省去了大量磁头寻址时间。

②pagecache,充分利用了非jvm的空闲内存,jvm重启后,pagecache仍然可用。

③零复制技术:不需要与user space交互,直接将序列化的文件写入磁盘。

由常规: 简化为:  

 

8.zookeeper在Kafka中的作用

①kafka集群中有一个broker会被zookeeper选举为Controller,负责管理broker的上下线、topic分区、副本分配、leader选举等工作。

② zookeeper中保存的主要的信息:

1. /brokers/ids/           [0,1,2]                          (临时节点)

2. /brokers/topics/first/partitions/0/state/"leader":1,"isr":[0,1,2]

....

 

以上信息会被kafkaController监听。实时更新leader和isr。

8.Producer API 

先导依赖:

<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.11.0.0</version>
</dependency>

1.流程图

后续sender会不断从RecordAccumulator中拉数据,发往topic中的各个分区。

相关参数:
batch.size:只有数据积累到batch.size之后,sender才会发送数据。
linger.ms:如果数据迟迟未达到batch.size,sender等待linger.ms之后就会发送数据。

代码:

1.没有回调函数

public class CustomProducer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop1:9092");//kafka集群,broker-list
        props.put("acks", "all");
        props.put("retries", 1);//重试次数
        props.put("batch.size", 16384);//批次大小
        props.put("linger.ms", 1);//等待时间
        props.put("buffer.memory", 33554432);//RecordAccumulator缓冲区大小,最大32M
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        //创建一个生产者对象
        Producer<String, String> producer = new KafkaProducer<>(props);

        //调用send方法
        for (int i = 0; i < 100; i++) {
            producer.send(new ProducerRecord<String, String>("first", Integer.toString(i), Integer.toString(i)));
        }

        //关闭
        producer.close();
    }
}

2.在send方法中添加回调方法(可用于记录日志等,消息发送失败会自动重试,不需要写在回调函数中)

public class CustomProducer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop102:9092");//kafka集群,broker-list
        props.put("acks", "all");
        props.put("retries", 1);//重试次数
        props.put("batch.size", 16384);//批次大小
        props.put("linger.ms", 1);//等待时间
        props.put("buffer.memory", 33554432);//RecordAccumulator缓冲区大小
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        Producer<String, String> producer = new KafkaProducer<>(props);
        for (int i = 0; i < 100; i++) {
            producer.send(new ProducerRecord<String, String>("first", Integer.toString(i), Integer.toString(i)), new Callback() {

                //回调函数,该方法会在Producer收到ack时调用,为异步调用
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    if (exception == null) {
                        System.out.println("success->" + metadata.offset());
                    } else {
                        exception.printStackTrace();
                    }
                }
            });
        }
        producer.close();
    }
}

3.同步发送(消息发送之后,阻塞当前线程,直到返回ack):

上述代码均为异步发送,实现同步仅需要在send方法后追加一个.get()方法即可(因为send方法返回的是一个Future对象,是一个callable线程)

producer.send(new ProducerRecord<String, String>("first", Integer.toString(i), Integer.toString(i))).get();

9.Consumer API

1.自动提交offset

public class CustomConsumer {

    public static void main(String[] args) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop102:9092");
        props.put("group.id", "test");
        props.put("enable.auto.commit", "true"); //不写的话默认为true
        props.put("auto.commit.interval.ms", "1000");//默认自动提交offset的时间
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

        //创建一个消费者
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

        //指定要消费的主题
        consumer.subscribe(Arrays.asList("first"));

        //调用poll方法轮询拉取数据
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);
            for (ConsumerRecord<String, String> record : records)
                System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
        }
    }
}


2.手动提交offset(以先消费为例)

先提交offset,后消费,在消费前如果consumer挂了,可能导致数据丢失。

先消费,后提交offset,在提交前如果consumer挂了,可能导致数据重复。

①同步提交

在调用poll方法后面:

while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);//消费者拉取数据
            for (ConsumerRecord<String, String> record : records) {
                System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
            }

            consumer.commitSync();//同步提交,当前线程会阻塞知道offset提交成功

        }

②异步提交

while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);//消费者拉取数据
            for (ConsumerRecord<String, String> record : records) {
                System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
            }

            consumer.commitAsync();//异步提交

        }

③带回调方法的异步提交

while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);//消费者拉取数据
            for (ConsumerRecord<String, String> record : records) {
                System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
            }

            consumer.commitAsync(new OffsetCommitCallback() {
                @Override
                public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
                    if (exception != null) {
                        System.err.println("Commit failed for" + offsets);
                    }
                }
            });//异步提交
        }

10 常见Kafka面试题汇总

1.ISR、AR、OSR代表什么
    ISR:与leader保持同步的follower集合
    AR:分区的所有副本
    OSR: 被暂时提出ISR的follower集合
    ISR + OSR =AR

2.Kafka中的HW、LEO等分别代表什么?
    LEO(Log End Offset):没个副本的最后条消息的offset
    HW(High Watermark):一个分区中所有副本最小的offset

3.Kafka中是怎么体现消息顺序性的?
    每个分区内,每条消息都有一个offset,故只能保证分区内有序。

4.Kafka中的分区器、序列化器、拦截器是否了解?它们之间的处理顺序是什么?
    拦截器的onSend()方法(acknowledgement方法是在返回ack之后调用的) -> 序列化器 -> 分区器

5.Kafka生产者客户端的整体结构是什么样子的?使用了几个线程来处理?分别是什么?
    结果见Producer API 的1.流程图
    两个线程:主线程和sender线程

6.“消费组中的消费者个数如果超过topic的分区,那么就会有消费者消费不到数据”这句话是否正确?
    正确,一个分区只能被一个消费者消费。

7.消费者提交消费位移时提交的是当前消费到的最新消息的offset还是offset+1?
    offset+1

8.重复消费和漏消费的场景
    重复:生产者:ack=-1;消费者:先消费,后返回ack。
    漏:生产者:ack=1;消费者,先返回ack,后消费。

9.当你使用kafka-topics.sh创建(删除)了一个topic之后,Kafka背后会执行什么逻辑?
    1)会在zookeeper中的/brokers/topics节点下创建一个新的topic节点,如:/brokers/topics/first
    2)触发Controller的监听程序
    3)kafka Controller 负责topic的创建工作,并更新metadata cache

10.topic的分区数可不可以增加或减少,如何操作
    可以增加,操作:bin/kafka-topics.sh --zookeeper localhost:2181/kafka --alter --topic topic-config --partitions 3
    不可以减少,被删除的分区数据难以处理。

11.Kafka有内部的topic吗?如果有是什么?有什么所用?
    _consumer_offsets,保存消费者offset

12.Kafka分区分配的概念?
    一个topic多个分区,一个消费者组多个消费者,故需要将分区分配个消费者(采用roundrobin、range两种分区策略,一般使用roundrobin,因为range可能会导致分配不均匀)

13.简述Kafka的日志目录结构?
    每个分区对应一个文件夹,文件夹的命名为topic-0,topic-1(topic名加分区号),内部为.log和.index文件

14.如果我指定了一个offset,Kafka Controller怎么查找到对应的消息?
    结合2.kafka文件存储机制中的第二张图,如果要找一个offset=9的offset,先定位segment,看index最后一位,因为6<9<12,所以定位segement-1 ,再通过文件名定义在index中的位置:9-6=3,找到3--699对应message-9。

15.聊一聊Kafka Controller的作用?
    整个Kafka集群的管理者,依赖zookeeper;负责管理集群broker的上下线,所有topic的分区副本分配和leader选举等工作。

16.Kafka中有那些地方需要选举?这些地方的选举策略又有哪些?
    partition leader(ISR),controller(先到先得)

17.失效副本是指什么?有那些应对措施?
    不能及时与leader同步,暂时踢出ISR,等其追上leader之后再重新加入。

18.Kafka的那些设计让它有如此高的性能?
    分区,并行读写;顺序写磁盘;0-copy,序列化文件发送,不到用户层进行拷贝。

19.Kafka如何实现Exactly Once
    ack = -1 即 at Least Once
    At Least Once + 幂等性= Exactly Once
    幂等性 设置全局ID,开启参数enable.idmpotence,设为true。

 


 

 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值