kafka简单复习
- 消息
- 生产者(
producer
):将消息放入对应主题的分区(partition
)中
- 键:一个标记(标记消息)
- 分区器:算法(将消息放入哪个分区)
- 消费者(
consumer
):订阅主题(Topic
),根据偏移量(第几个消息)获取消息,每一个消费者都属于某一个消费者组(consumer group
),同一个消费者组的消费者只能有一个消费者消费某个分区的消息,不同消费者组中的消费者可以消费同一个分区消息。 kafka
服务器:broker
,一个broker
有多个主题,一个主题有多个分区- 多个
broker
组成kafka
集群:需要一个broker
来充当控制器,其他broker
相互备份,一个宕机另一个马上可以用(高可用) - 每个主题创建副本的数量不能多于
broker
的数量
springboot整合kafka
public class Producer{
String serverList="";
String topic = "";
String groupId = "";
Properties properties = new Properties();
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,serverList);
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
for(int i = 0; i < 5; i++){
kafkaProducer.send(new ProducerRecord<>(topic,"demo" + i)).get();
}
}
public class Consumer{
String serverList="";
String topic = "";
String groupId = "";
Properties properties = new Properties();
properties.put(ProducerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.put(ProducerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.put();
properties.put("group.id", groupId);
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);
kafkaConsumer.subscribe(Collections.singletonList(topic));
while(true){
ConsumerRecords<String, String> records = kafkaConsumer.poll(Duration.ofMillis(1000));
if(Objects.isNull(records)){
continue;
}
for(onsumerRecord<String, String> record: records){
bjectMapper objectMapper = new ObjectMapper();
String jsonString = record.get("");
MyObject myObject = objectMapper.readValue(jsonString, MyObject.class);
}
}
}
public class ProducerSendListener implment ProducerListener{
public void onSuccess(){
producerRecord.key();
}
public void onError(){
producerRecord.key();
}
}
public class KafkaTest{
@Autoware
private KafkaTemplate<String,String> kafkaTemplate;
@Autoware
private ProductSendListener productSendListener;
String topic = "topic"
void sendMsg(){
kafkaTemplate.setProducerListener(productSendListener);
kafkaTemplate.send(topic,"Hello");
}
}
发送消息的三种方式
发送即忘
- 默认的
send()
方法就是发送即忘:发送消息后不会关注消息是否发送成功,不对结果负责,异步发送。 - 场景:数据丢失对业务影响不大,如日志数据,并且要保持高吞吐量。
同步发送
- 借助
send()
方法返回的future对象,调用get()
方法同步阻塞,并配合acks
参数的配置默认使用1
、还有0
、all
0
:发送后成功,无需等待确认leader写入成功(与异步不同的是:还是需要返回对象,只是再写入成功前就返回)1
:生产者接受到分区leader写入成功,才解除阻塞状态-1(all)
:确保分区中所有副本全部写入成功才返回成功状态
- 场景:需要确保消息发送成功并成功写入的场景
properties.put(ProducerConfig.ACKS_CONF,1);
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
for(int i = 0; i < 5; i++){
RecordMetadata metadata = kafkaProducer.send(new ProducerRecord<>(topic,"demo" + i)).get();
}
异步发送+回调
send()
方法本身就是异步的,无法判断消息是否发送成功,出现异常的时候没办法捕获,所以就采用回调的方法实现,回调函数包括两个参数,这两个参数是互斥的。
exception
为空则meta
不为空,代表发送消息成功exception
不为空则meta
肯定为空,代表消息发送失败,出现异常
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
for(int i = 0; i < 5; i++){
kafkaProducer.send(new ProducerRecord<>(topic,"demo" + i), new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null) {
System.err.println("发送消息失败:" + exception.getMessage());
} else {
System.out.println("消息发送成功,topic:" + metadata.topic() + ", partition:" + metadata.partition() + ", offset:" + metadata.offset());
}
}
});
}