Kafka 是分布式发布-订阅消息系统。它最初由 LinkedIn 公司开发,以后成为 Apache 项目的一部分。面试
Kafka 是一个分布式的,可划分的,冗余备份的持久性的日志服务。它主要用于处理活跃的流式数据。缓存
Kafka 的总体架构很是简单,是显式分布式架构,主要由 producer、broker(kafka)和 consumer 组成。网络
Producer(生产者)能够将数据发布到所选择的 topic(主题)中。生产者负责将记录分配到 topic 的哪个 partition(分区)中。可使用循环的方式来简单地实现负载均衡,也能够根据某些语义分区函数(如记录中的key)来完成。session
Consumer(消费者)使用一个consumer group(消费组)名称来进行标识,发布到 topic 中的每条记录被分配给订阅消费组中的一个消费者实例。消费者实例能够分布在多个进程中或者多个机器上。架构
在讨论 kafka 是否丢消息前先来了解一下什么是消息传递语义。负载均衡
message delivery semantic 也就是消息传递语义,简单说就是消息传递过程当中消息传递的保证性。主要分为三种:异步
理想状况下确定是但愿系统的消息传递是严格 exactly once,也就是保证不丢失、只会被处理一次,可是很难作到。async
回到主角 Kafka,Kafka 有三次消息传递的过程:分布式
在这三步中每一步都有可能会丢失消息,下面详细分析为何会丢消息,如何最大限度避免丢失消息。函数
先介绍一下生产者发送消息的通常流程(部分流程与具体配置项强相关,这里先忽略):
生产者采用 push 模式将数据发布到 broker,每条消息追加到分区中,顺序写入磁盘。消息写入 Leader 后,Follower 是主动与 Leader 进行同步。
Kafka 消息发送有两种方式:同步(sync)和异步(async),默认是同步方式,可经过 producer.type 属性进行配置。
Kafka 经过配置 request.required.acks 属性来确认 Producer 的消息:
若是 acks 配置为 0,发生网络抖动消息丢了,生产者不校验 ACK 天然就不知道丢了。
若是 acks 配置为 1 保证 leader 不丢,可是若是 leader 挂了,刚好选了一个没有 ACK 的 follower,那也丢了。
若是 acks 配置为 all 保证 leader 和 follower 不丢,可是若是网络拥塞,没有收到 ACK,会有重复发的问题。
Kafka Broker 接收到数据后会将数据进行持久化存储,你觉得是下面这样的:
没想到是这样的:
操做系统自己有一层缓存,叫作 Page Cache,当往磁盘文件写入的时候,系统会先将数据流写入缓存中,至于何时将缓存的数据写入文件中是由操做系统自行决定。
Kafka 提供了一个参数 producer.type 来控制是否是主动 flush,若是 Kafka 写入到 mmap 以后就当即 flush 而后再返回 Producer 叫同步 (sync);写入 mmap 以后当即返回 Producer 不调用 flush 叫异步 (async)。
Kafka 经过多分区多副本机制中已经能最大限度保证数据不会丢失,若是数据已经写入系统 cache 中可是还没来得及刷入磁盘,此时忽然机器宕机或者掉电那就丢了,固然这种状况很极端。
消费者经过 pull 模式主动的去 kafka 集群拉取消息,与 producer 相同的是,消费者在拉取消息的时候也是找 leader 分区去拉取。
多个消费者能够组成一个消费者组(consumer group),每一个消费者组都有一个组id。同一个消费者组的消费者能够消费同一 topic 下不一样分区的数据,可是不会出现多个消费者消费同一分区的数据。
消费者消费的进度经过 offset 保存在 kafka 集群的 __consumer_offsets 这个 topic 中。
消费消息的时候主要分为两个阶段:
先 commit 再处理消息。若是在处理消息的时候异常了,可是 offset 已经提交了,这条消息对于该消费者来讲就是丢失了,不再会消费到了。
先处理消息再 commit。若是在 commit 以前发生异常,下次还会消费到该消息,重复消费的问题能够经过业务保证消息幂等性来解决。
那么问题来了,kafka到底会不会丢消息?答案是:会!
Kafka可能会在三个阶段丢失消息:
在生产环境中严格作到 exactly once 实际上是难的,同时也会牺牲效率和吞吐量,最佳实践是业务侧作好补偿机制,万一出现消息丢失能够兜底。