得物面试:Kafka消息0丢失,如何实现?

得物面试:Kafka消息0丢失,如何实现?

尼恩说在前面

在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:

Kafka消息0丢失,如何实现?

kafka如何保证消息不丢失?

最近有小伙伴在面试得物,又遇到了相关的面试题。小伙伴懵了,因为没有遇到过,所以支支吾吾的说了几句,面试官不满意,面试挂了。

所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。

当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V140版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到文末公号【技术自由圈】获取

在这里插入图片描述

消息的发送流程

一条消息从生产到被消费,将会经历三个阶段:

  • 生产阶段,Producer 新建消息,而后经过网络将消息投递给 MQ Broker
  • 存储阶段,消息将会存储在 Broker 端磁盘中
  • 消息阶段, Consumer 将会从 Broker 拉取消息

以上任一阶段, 都可能会丢失消息,只要这三个阶段0丢失,就能够完全解决消息丢失的问题。

生产阶段如何实现0丢失方式

从架构视角来说, kafka 生产者之所以会丢消息,和Producer 的高吞吐架构有关。

Producer 的高吞吐架构

Producer 的高吞吐架构: 异步发生+ 批量发送。

Kafka的Producer发送消息采用的是异步发送的方式。
在消息发送的过程中,涉及到了两个线程和一个队列:

  • 业务线程 和 Sender线程
  • 以及一个消息累积器 : RecordAccumulator。

在这里插入图片描述

Kafka Producer SDK会创建了一个消息累积器 RecordAccumulator,里边使用 双端队列 缓存消息, 业务线程 将消息加入到 RecordAccumulator ,业务线程就返回了。

这就是业务发送的妙处, 注意, 业务线程就返回了,但是底层的发送工作,还没开始。

谁来负责底层发送呢? Sender线程。

Sender线程不断从 RecordAccumulator 中拉取消息,负责发送到Kafka broker。

在这里插入图片描述

这回我们明白了, 原来机关在这里:

  • kafka在发送消息时,是由底层的SEND线程进行消息的批量发送,不是由业务代码线程执行发送的。

  • 业务代码线程执行完send方法后,就返回了。

    很多小伙伴 没有写个kafka发消息的代码,下面有一个demo,大家一看就明白了:

package org.example;

import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.Properties;

public class KafkaProducerExample {
    public static void main(String[] args) {

        Producer<String, String> producer = getProducer();

        // Kafka 主题名称
        String topic = "mytopic";

        // 发送消息
        String message = "Hello, Kafka!";
        ProducerRecord<String, String> record = new ProducerRecord<>(topic, message);
        producer.send(record);

        // 关闭 Kafka 生产者
        producer.close();
    }

    private static Producer<String, String> getProducer() {
        Properties properties = getProperties();
        // 创建 Kafka 生产者
        Producer<String, String> producer = new KafkaProducer<>(properties);
        return producer;
    }

    private static Properties getProperties() {
        // Kafka 服务器地址和端口
        String bootstrapServers = "localhost:9092";
        // Kafka 生产者配置
        Properties properties = new Properties();
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        return properties;
    }
}

消息到底发送给broker侧没有了?通过send方法其实是无法知道的。

上面的代码,创建消息之后,就开始发送消息了

 ProducerRecord<String, String> record = new ProducerRecord<>(topic, message);
producer.send(record);

其实,如果要想知道发现的结果,可以通过send方法的 Future实例去实现:

Future<RecordMetadata> future = producer.send(record);

Producer#send方法是一个异步方法,即它会立即返回一个Future对象,而不会等待消息发送完成。

可以使用Future对象来异步处理发送结果,例如等待发送完成或注册回调函数来处理结果。具体的办法是,给Future去注册回调函数处理结果,这样可以实现非阻塞的方式处理发送完成的回调。

Future去注册回调函数处理结果,下面是一个示例代码:

future.addCallback(new ListenableFutureCallback<RecordMetadata>() {
    @Override
    public void onSuccess(RecordMetadata metadata) {
        System.out.println("消息发送成功,分区:" + metadata.partition() + ",偏移量:" + metadata.offset());
    }

    @Override
    public void onFailure(Throwable ex) {
        System.err.println("消息发送失败:" + ex.getMessage());
    }
});

还没有其他的方法, 获取发送的处理结果呢?

其实,Producer#send方法有两个重载版本, 具体如下:

Future<RecordMetadata> send(ProducerRecord<K, V> record)
Future<RecordMetadata> send(ProducerRecord<K, V> record, Callback callback)

除了上面尼恩给大家介绍的是一个参数的版本, 实际上还有一个两个参数的版本:

Future<RecordMetadata> send(ProducerRecord<K, V> record, Callback callback)

通过这个版本, 大家可以注册回调函数的方式,完成发送结果的处理。通过回调函数版本,更好的实现非阻塞的方式处理发送完成的回调。

参考的代码如下:

   producer.send(record, new Callback() {
            @Override
            public void onCompletion(RecordMetadata metadata
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值