1. kafka定义
- kafka是分布式的基于发布、订阅模式的消息队列,主要用于大数据实时处理。
2. 消息队列的作用
3. 消息队列的两种模式
- 点对点模式
- 一对一,生产者生产的消息只能被对应的消费者消费。
- 数据不进行持久化,消息不能重复消费。
- 发布/订阅模式
- 一对多,生产者发布的主题可被多个消费者订阅。
- 消息持久化,但不是永久,默认一周清理一次。
4. kafka基础架构
- 为方便扩展,并提高吞吐量,一个topic可以分为多个分区。
- 配合分区的设计,提出消费组的概念,组内每个消费者进行消费。
- 为提高可用性,每个partition增加若干副本(leader follower)
- 相关概念
- Producer
- Consumer
- Consumer Group
- Broker
- Topic
- Partition
- Replica: 副本
- leader
- follower
5. kafka集群配置
broker.id=0
logs.dir=/opt/kafka/logs
zookeeper.connect=hadoop102:2181,hadoop103:2181
- 配置环境变量
- export KAFKA_HOME=
- export PATH=
6. kafka常用命令
- kafka-topics.sh 曾删改查topic
- kafka-topics.sh --zookeeper hadoop102:2181 --cerate --topic first --partitions 3 --replication-factor 2
创建主题first,分区3,副本数2. - kafka-topics.sh --zookeeper hadoop102:2181 --describe --topic first
主题描述
- kafka-producer.sh 生产者
- kafka-producer.sh --topic first --broker-list hadoop101:9092,hadoop102:9092
- 生产者,指定要发布的节点
- kafka-console-consumer.sh --topic first --bootstrap-server hadoop102:9092
- kafka-topics.sh --zookeeper hadoop102:2181 --alter --partitions 4 --topic first
- 修改分区数为4 ,topic的分区数只能增加,不能减少。
7. kafka存储机制
- 一个topic分为多个partition。
- 每个partition一个log文件,log文件的特点是可以不停追加。
- 每个log文件分为多个片(segment),根据大小进行追加。
- 底层存储的是.log文件和.index文件,.index是协助定位.log文件
- log文件的命名是以每个segment的第一条消息的offset命名的。
- index文件的命名与log文件的命名对应,文件内有两列数据,第一列是当前segment的消息序号,
第二列是每条消息的开头对应的log文件的偏移量。
- 如何找到offset=3的Message?
- 首先找到文件,看3处于哪两个文件之间。
- 3-(文件命名的末尾)得到 index文件中的消息序号,进而得到对应log文件中的偏移量。
8. producer分区原则
- 分区的好处
- producer发送消息封装的是ProducerRecord对象。
- 分区原则
- 在指明partition的情况下,直接将指明的值作为partition的值。
- 未指明partition但有key的情况下,将key的hash值与topic的partition数进行取余得到partition值
- 既没有partition值有没有key值的情况下,第一次调用是随机生成一个整数,后面每次调用在这个整数上自增,
将这个值与topic可用的partition总数取余得到partition值,就是常说的round-robin算法。
9. consumer相关
- 消费方式
- pull (kafka采用的方式)根据自己的消费能力拉取数据
- push
- 分区分配策略
- roundrobin (默认采用)
- range (有可能分配不均)
- offset维护
- 0.9版本之前,offset保存在zookeeper中。
- 0.9版本之后,offset默认保存在kafka一个内置的topic中。
10. 高效读写
11. zookeeper的作用
- kafka的Controller依赖于zookeeper
12. 异步发送
- main线程,sender线程,线程共享变量RecordAccmulator。
public class CustomProducer{
public static void main(String[] args){
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVER_COINFIG,"hadoop102:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
props.put(ProducerConfig.ACKS_CONFIG,"all");
props.put(ProducerConfig.BATCH_SIZE_CONFIG,16384);
props.put(ProducerConfig.LINGER_MS_CONFIG,1);
KafkaProcuder producer = new KafkaProducer<String, String>(props);
for(int i=0; i < 1000; i++){
producer.send(new ProducerRecord<String,String>("first",i,"message"+i));
}
producer.close();
}
}
13. 同步发送
public class SyncProducer(){
public static void main(String[] args){
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVER_COINFIG,"hadoop102:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
props.put(ProducerConfig.ACKS_CONFIG,"all");
props.put(ProducerConfig.BATCH_SIZE_CONFIG,16384);
props.put(ProducerConfig.LINGER_MS_CONFIG,1);
KafkaProcuder producer = new KafkaProducer<String, String>(props);
for(int i=0; i < 1000; i++){
RecordMetadata first = producer.send(new ProducerRecord<String,String>("first",i,"message"+i)).get();
System.out.println("meta="+meta.offset());
}
producer.close();
}
}
14. 消费者api
public class CustomConsumer(){
public static void main(String[] args){
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_CONFIG_SERVER,"hadoop102:9092");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class.getName());
props.put(ConsumerConfig.GOURP_ID_CONFIG,"1205");
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT,"false");
KafkaConsumer<String,String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Array.asList("first"));
while(true){
ConsumcerRecords<String, String> records = consumer.pull(100);
for(ConsumerRecord<String,String> record:records){
System.out.println("topic=" + record.topic()+"offset="+record.offset()+"value="+record.value());
}
}
consumer.commitAsync();
}
}
15. 拦截器
public class TimeInterceptor implements ProducerInterceptor<String,String>{
private long successNumber = 0;
private long errorNumber = 0;
@override
public ProducerRecord<String, String> onSend(ProducerRecord<String,String> record){
return new ProducerRecord<String, String>(record.topic(),record.partition(),
record.timestamp(),record.key(),
System.currentTimeMillis()+record.value());
}
@override
public void on Acknowledgement(RecordMetadata metadata, Exception exception){
if(exception == null){
successNumber++;
}else{
errorNumber++;
}
}
@override
public void close(){
}
@override
public void configure(Map<String, ?> configs){
}
}
16. kafka监控
- 常用监控工具
- kafka monitor
- kafka manager
17. kafka面试题
- ISR,AR分别是什么?
- HW,LEO分别是什么?
- 每个分区的最后的offset
- 所有分区中最后的offset的最小值
- 怎么体现消息顺序性
- kafka只能保证分区内有序
- 要保证所有的消息有序,可以只用一个分区
- kafka中的分区器,序列化,拦截器的了解以及执行顺序
- 生产者客户端的结构是什么样子?用了几个线程来处理?分别是什么?
- 主线程,sender线程和RecordAccumulator线程共享变量。
- 消费者个数如果超过topic分区,那么就会有消费者消费不到数据,怎么理解?
- 消费者提交消费位移是当前消息的offset还是offset+1?
- 哪些情形会造成重复消费?
- ack=-1的时候
- follower同步完成,ack未返回leader挂掉,新的leader就会重复消费。
- 先消费,后提交offset。
- 哪些情形会造成漏消费?
- 创建topic的步骤
- 在zookeeper的broker/topics节点下创建一个新的topic节点
- 触发Controller监听程序
- kafka controller负责topic的创建工作,并更新metadata cache.
- topic的分区数可以增加吗?
- 可以增加,不可以减少,可以通过alter命令,也可以通过管理工具
- kafka有内部的topic吗?
- 分区分配的概念?
- kafka controller的作用?
- 整个kafka集群的管理者
- 依赖zookeeper来完成
- leader的选举
- controller选举等等
- 什么地方需要选举?
- controller选举,先到先得
- leader选举
- 失效副本是什么?怎么应对?
- kafka 的哪些设计提高性能