一、Broker扩展
- 配置文件:server.properties
- Log Flush Policy:设置数据flush到磁盘的时机
- Log Retention Policy:设置数据保存周期,默认7天
二、Producer扩展
- partitioner:根据用户设置的算法来计算发送到哪个分区,默认是随机发送到不同分区
- 数据通讯方式:同步发送和异步发送
2.1、Kafka如何保证数据不丢
- acks:默认是1,表示需要Leader节点回复收到消息
- acks:all,表示需要所有Leader+副本节点回复收到消息(acks=-1)
- acks:0,表示不需要任何节点回复
三、Topic、Partition扩展
- 每个Partition在存储层面是Append Log文件,新消息都会被直接追加到log文件的尾部,每条消息在log文件中的位置称为Offset(偏移量)
- 越多Partition可以容纳更多的Consumer,有效提升并发消费的能力
- 业务类型增加需要增加Topic、数据量大需要增加Partition
四、Message扩展
-
offset,类型为long,表示此消息在一个Partition中的起始位置,可以认为Offset是Partition中Message的id,自增
-
MessageSize,类型为int32,表示此消息的字节大小
-
data,类型为bytes,表示message的具体内容
五、存储策略
- 在Kafka中每个Topic包含1到多个Partition,每个Partition存储一部分Message。每条Message包含三个属性,其中一个是Offset
- Offset相当于Partition中这个Message的唯一id,那么如何通过id高效的找到Message?
- 通过分段+索引
六、容错机制
-
当Kafka集群中的一个Broker节点宕机,会出现什么现象?
zookeeper会自动选举其他机器
七、代码演示
package com.strivelearn.java.kafka;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
/**
* @author strivelearn
* @version ProducerDemo.java, 2022年12月05日
*/
public class ProducerDemo {
public static void main(String[] args) {
Properties properties = new Properties();
//指定Kafka的broker地址
properties.put("bootstrap.servers", "192.168.234.100:9092");
//指定key-value数据的序列化格式
// properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("key.serializer", StringSerializer.class.getName());
properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("acks", "all");
//创建Kafka生产者
KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
//向topic中生产数据
String topic = "my-topic";
producer.send(new ProducerRecord<String, String>(topic, "hello java kafka producer"));
//关闭链接
producer.close();
}
}
package com.strivelearn.java.kafka;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;
/**
* @author strivelearn
* @version ConsumerDemo.java, 2022年12月05日
*/
public class ConsumerDemo {
public static void main(String[] args) {
Properties properties = new Properties();
//指定Kafka的broker地址
properties.put("bootstrap.servers", "192.168.234.100:9092");
//指定key-value数据的反序列化格式
// properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("key.deserializer", StringDeserializer.class.getName());
properties.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
//指定消费组
properties.put("group.id", "con-1");
//创建Kafka消费者
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties);
Collection<String> topic = new ArrayList<String>();
topic.add("my-topic");
consumer.subscribe(topic);
while (true) {
ConsumerRecords<String, String> poll = consumer.poll(Duration.ofSeconds(1));
for (ConsumerRecord<String, String> stringStringConsumerRecord : poll) {
System.out.println(stringStringConsumerRecord);
}
}
}
}
ConsumerRecord(topic = my-topic, partition = 0, leaderEpoch = 0, offset = 4, CreateTime = 1670298623231, serialized key size = -1, serialized value size = 25, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = hello java kafka producer)
7.1消费者扩展
上面的方式,我们只能消费消费者起来后的数据,没办法获取消费者起来之前的数据,需要进行改造。改成earliest
//开启自动提交offset功能,默认是开启的
properties.put("enable.auto.commit", "true");
//自动提交offset的时间间隔,单位是毫秒
properties.put("auto.commit.interval.ms", "5000");
/**
* 正常情况下,Kafka消费数据的流程是这样的
* 先根据group.id指定的消费组到kafka中查找之前保存的offset信息
* 如果查找到了,说明之前使用这个消费组消费过数据,则根据之前保存的offset继续进行消费
* 如果没查找到(说明第一次消费),或者查找到了,但是查找到的那个offset对应的数据已经不存在了
* 这个时候消费者该如何消费数据?
* (因为kafka默认只会保存7天的数据,超过时间数据会被删除,此时会根据auto.offset.reset的值执行不同的消费逻辑)
* 这个参数有三个值:
* 1.earliest:表示从最早的数据开始消费(从头消费)
* 2.latest:默认。从最新的数据开始消费
* 3.none:根据指定的group.id没有找到之前消费的offset信息,则抛出异常
*/
//一般在实时计算场景,使用latest
properties.put("auto.offset.reset", "latest");
7.2、Consumer消费offset查询
- kafka0.9版本之前,消费者的offset信息保存在zookeeper中
- 从kafka0.9开始,使用了新的消费api,消费者的信息会保存在kafka里面的
_consumer_offsets这个topic中
7.3、Consumer消费顺序
- 当一个消费者消费一个partition时候,消费的数据顺序和此partition数据的生产顺序是一致的
- 当一个消费者消费多个partition时候,消费者按照partition的顺序,首先消费一个partition,当消费完一个partition最新的数据后再消费其他partition中的数据
- 一个消费者消费多个partition,只能保证消费的数据顺序在一个partition内是有序的
7.4、Kafka的三种语义
-
至少一次:at-least-once
这种情况就存在消费多次,那么我们可以把properties.put("enable.auto.commit", "false");设置为false。在我们消费完之后,我们手动进行提交consumer.commitSync()
-
至多一次:at-most-once
-
仅一次:exactly-once