乐趣区

关于java:面试官竟然问我消息队列为啥会丢失消息幸亏我总结了全套八股文

一个挺着啤酒肚,身穿格子衫,发际线重大后移的中年男子,手拿着保温杯,胳膊夹着 MacBook 向你走来,看样子是架构师级别。

面试开始,直入正题。

面试官: 我看到你的简历上写着我的项目中用到了音讯队列,还用的是 kafka,你有遇到过音讯队列失落音讯的状况吗?

我: [疑难] 音讯队列还能失落音讯?那谁还用音讯队列!你是不是搞错了?我没遇到过失落音讯的状况,也没思考过这个问题。

面试官: 嗯 …,小伙子,看来有些面试套路,你还是不太懂。明天面试就先到这里吧!给你的简历,我送你下楼。

我去!面试还有啥套路?
能不能少一点套路,多一点真挚!
难道都要去背一遍八股文能力加入面试?
好吧,我去瞅一眼一灯总结的面试八股文。

我: 音讯队列发送音讯和生产音讯的过程,共分为三段,生产过程、服务端长久化过程、生产过程,如下图所示。

这三个过程都有可能弄丢音讯。

面试官: 嗯,音讯失落的具体起因是什么?怎么避免失落音讯呢?

我: 我具体说一下这种状况:

一、生产过程失落音讯

失落起因:个别可能是网络故障,导致音讯没有发送进来。

解决方案:重发就行了。

因为 kafka 为了进步性能,采纳了异步发送音讯。咱们只有获取到发送后果,能力确保音讯发送胜利。
有两个计划能够获取发送后果。

一种是 kafka 把发送后果封装在 Future 对象中,我能够应用 Future 的 get 办法同步阻塞获取后果。

Future<RecordMetadata> future = producer.send(new ProducerRecord<>(topic, message));
try {RecordMetadata recordMetadata = future.get();
    if (recordMetadata != null) {System.out.println("发送胜利");
    }
} catch (Exception e) {e.printStackTrace();
}

另一种是应用 kafka 的 callback 函数获取返回后果。

producer.send(new ProducerRecord<>(topic, message), new Callback() {
    @Override
    public void onCompletion(RecordMetadata metadata, Exception exception) {if (exception == null) {System.out.println("发送胜利");
        } else {System.out.println("发送失败");
        }
    }
});

如果发送失败了,有两种重试计划:

  1. 手动重试
    在 catch 逻辑或 else 逻辑中,再调用一次 send 办法。如果还不胜利怎么办?
    在数据库中建一张异样音讯表,把失败音讯存入表中,而后搞个异步工作重试,便于管制重试次数和间隔时间。
  2. 主动重试
    kafka 反对主动重试,设置参数如下,当集群 Leader 选举中或者 Follower 数量有余等起因返回失败时,就能够主动重试。

    # 设置重试次数为 3
    retries = 3
    # 设置重试距离为 100ms
    retry.backoff.ms = 100

    个别咱们不会用 kafka 主动重试,因为超过重试次数,还是会返回失败,还须要咱们手动重试。

二、服务端长久化过程失落音讯

为了保障性能,kafka 采纳的是异步刷盘,当咱们发送音讯胜利后,Broker 节点在刷盘之前宕机了,就会导致音讯失落。

当然咱们也能够设置刷盘频率:

# 设置每 1000 条音讯刷一次盘
flush.messages = 1000
# 设置每秒刷一次盘
flush.ms = 1000

先遍及一下 kafka 集群的架构模型:

kafka 集群由多个 broker 组成,一个 broker 就是一个节点(机器)。
一个 topic 有多个 partition(分区),每个 partition 散布在不同的 broker 下面,能够充分利用分布式机器性能,扩容时只须要加机器、加 partition 就行了。

一个 partition 又有多个 replica(正本),有一个 leader replica(主正本)和多个 follower replica(从正本),这样设计是为了保证数据的安全性。

发送音讯和生产音讯都在 leader 下面,follower 负责定时从 leader 下面拉取音讯,只有 follower 从 leader 下面把这条音讯拉取回来,才算生产者发送音讯胜利。

kafka 为了放慢长久化音讯的性能,把性能较好的 follower 组成一个 ISR 列表(in-sync replica),把性能较差的 follower 组成一个 OSR 列表(out-of-sync replica),ISR+OSR=AR(assigned repllicas)。
如果某个 follower 一段时间没有向 leader 拉取音讯,落后 leader 太多,就把它移出 ISR,放到 OSR 之中。
如果某个 follower 追上了 leader,又会把它从新放到 ISR 之中。
如果 leader 挂掉,就会从 ISR 之中选一个 follower 做 leader。

为了晋升长久化音讯性能,咱们能够进行一些设置:

# 如果 follower 超过一秒没有向 leader 拉取音讯,就把它移出 ISR 列表
rerplica.lag.time.max.ms = 1000
# 如果 follower 落后 leader 一千条音讯,就把它移出 ISR 列表
rerplica.lag.max.messages = 1000

# 至多保障 ISR 中有 3 个 follower
min.insync.replicas = 3

# 异步音讯,不须要 leader 确认,立刻给生产者返回发送胜利,失落音讯概率较大
asks = 0
# leader 把音讯写入本地日志中,不会等所有 follower 确认,就给生产者返回发送胜利,小概率失落音讯
asks = 1
# leader 须要所有 ISR 中 follower 确认,才给生产者返回发送胜利,不会失落音讯
asks = -1 或者 asks = all

三、生产过程失落音讯

kafka 中有个 offset 的概念,consumer 从 partition 中拉取音讯,consumer 本地解决实现后须要 commit 一下 offset,示意生产实现,下次就不会再拉取到这条音讯。
所以咱们须要敞开主动 commit offset 的配置,避免 consumer 拉到音讯后,服务宕机,导致音讯失落。

enable.auto.commit = false

面试官: 还得是你,就你总结的全,我都想不那么全,今天来下班吧,薪资 double。

本文知识点总结:

文章继续更新,能够微信搜一搜「一灯架构」第一工夫浏览更多技术干货。

退出移动版