定义
Kafka
是一个分布式流媒体平台,kafka
官网:http://kafka.apache.org/
Kafka
是一种高吞吐量、分布式、基于发布/订阅
的消息系统,最初由 LinkedIn
公司开发,使用Scala
语言编写,目前是Apache
的开源项目。
流媒体平台有三个关键功能:
- 发布和订阅记录流,类似于消息队列或企业消息传递系统。
- 以容错的持久方式存储记录流。
- 记录发生时处理流。
Kafka
通常用于两大类应用:
- 构建可在系统或应用程序之间可靠获取数据的实时流数据管道
- 构建转换或响应数据流的实时流应用程序
Kafka名词
下面是Kafka中涉及到的相关概念:
broker
:Kafka 服务器,负责消息存储和转发topic
:消息类别,Kafka 按照topic 来分类消息(即使如此,kafka仍然有点对点和广播发布类型)partition
:topic 的分区,一个 topic 可以包含多个 partition,topic 消息保存在各个partition 上offset
:消息在日志中的位置,可以理解是消息在 partition 上的偏移量,也是代表该消息的唯一序号Producer
:消息生产者Consumer
:消息消费者Consumer Group
:消费者分组,每个Consumer 必须属于一个 groupZookeeper
:保存着集群 broker、topic、partition 等 meta 数据;另外,还负责 broker 故障发现,partition leader 选举,负载均衡等功能
Kafka核心API
Kafka有四个核心API:
-
Producer API(生产者API)允许应用程序发布记录流至一个或多个kafka的topics(主题)
-
Consumer API(消费者API)允许应用程序订阅一个或多个topics(主题),并处理所产生的对他们记录的数据流。
-
Streams API(流API)允许应用程序充当流处理器,从一个或多个topics(主题)消耗的输入流,并产生一个输出流至一个或多个输出的topics(主题),有效地变换所述输入流,以输出流。
-
Connector API(连接器API)允许构建和运行kafka topics(主题)连接到现有的应用程序或数据系统中重用生产者或消费者。例如,关系数据库的连接器可能捕获对表的每个更改。
在Kafka中,客户端和服务器之间的通信是通过简单,高性能,语言无关的TCP协议完成的。此协议已版本化并保持与旧版本的向后兼容性。Kafka提供Java客户端,但客户端有多种语言版本。
Kafka相关组件介绍
Kafka之Topic
Topic 是生产者发送消息的目标地址,是消费者的监听目标
一个服务可以监听、发送多个 Topics
Kafka 中有一个consumer-group(消费者组)的概念。
这是一组服务,扮演一个消费者
如果是消费者组接收消息,Kafka 会把一条消息路由到组中的某一个服务
这样有助于消息的负载均衡,也方便扩展消费者。Topic 扮演一个消息的队列。首先,一条消息发送了。然后,这条消息被记录和存储在这个队列中,不允许被修改,接下来,消息会被发送给此 Topic 的消费者。但是,这条消息并不会被删除,会继续保留在队列中。像之前一样,这条消息会发送给消费者、不允许被改动、一直呆在队列中。(消息在队列中能呆多久,可以修改 Kafka 的配置)
Kafka之 Partitions分区
上面 Topic 的描述中,把 Topic 看做了一个队列,实际上,一个 Topic 是由多个队列组成的,被称为Partition(分区)。这样可以便于 Topic 的扩展,生产者发送消息的时候,这条消息会被路由到此Topic 中的某一个 Partition。消费者监听
的是所有分区。生产者发送消息时,默认是面向 Topic
的,由 Topic
决定放在哪个 Partition,默认使用轮询策略
也可以配置 Topic
,让同类型的消息都在同一个 Partition。例如,处理用户消息,可以让某一个用户所有消息都在一个 Partition。例如,用户1发送了3条消息:A、B、C,默认情况下,这3条消息是在不同的 Partition 中(如 P1、P2、P3)。在配置之后,可以确保用户1的所有消息都发到同一个分区中(如 P1)
这个功能有什么用呢?
这是为了提供消息的【有序性】。
消息在不同的 Partition 是不能保证有序的,只有一个 Partition 内的消息是有序的
Topics主题 和 Partitions分区
一个Topic
可以认为是一类消息,每个topic
将被分成多个partition
(区),每个partition
在存储层面是append log文件
主题是发布记录的类别或订阅源名称。Kafka
的主题总是多用户; 也就是说,一个主题可以有零个,一个或多个消费者订阅写入它的数据。
对于每个主题,Kafka
集群都维护一个如下所示的分区日志:
每个分区都是一个有序的,不可变的记录序列,不断附加到结构化的提交日志中。分区中的记录每个都分配了一个称为偏移的顺序ID号
,它唯一地标识分区中的每个记录。
Kafka集群持久保存所有已发布的记录 - 无论是否已使用 - 使用可配置的保留期。例如,如果保留策略设置为两天,则在发布记录后的两天内,它可供使用,之后将被丢弃以释放空间。Kafka的性能在数据大小方面实际上是恒定的,因此长时间存储数据不是问题。
实际上,基于每个消费者保留的唯一元数据是该消费者在日志中的偏移或位置。这种偏移由消费者控制:通常消费者在读取记录时会线性地提高其偏移量,但事实上,由于该位置由消费者控制,因此它可以按照自己喜欢的任何顺序消费记录。例如,消费者可以重置为较旧的偏移量来重新处理过去的数据,或者跳到最近的记录并从现在
开始消费。
这些功能组合意味着Kafka 消费者consumers
非常cheap
- 他们可以来来往往对集群或其他消费者没有太大影响。例如,可以使用我们的命令行工具“tail”任何主题的内容,而无需更改任何现有使用者所消耗的内容。
日志中的分区有多种用途。首先,它们允许日志扩展到超出适合单个服务器的大小。每个单独的分区必须适合托管它的服务器,但主题可能有许多分区,因此它可以处理任意数量的数据。其次,它们充当了并行性的单位 - 更多的是它
Distribution分配
一个Topic
的多个partitions
,被分布在kafka
集群中的多个server
上;每个server
(kafka实例)负责partitions中消息的读写操作;此外kafka
还可以配置partitions
需要备份的个数(replicas),每个partition
将会被备份到多台机器上,以提高可用性.
基于replicated
方案,那么就意味着需要对多个备份进行调度;每个partition都有一个server为leader
;leader
负责所有的读写操作,如果leader失效,那么将会有其他follower
来接管(成为新的leader);follower
只是单调的和leader跟进,同步消息即可…由此可见作为leader的server承载了全部的请求压力,因此从集群的整体考虑,有多少个partitions
就意味着有多少个"leader",kafka会将"leader"均衡的分散在每个实例上,来确保整体的性能稳定
Producers生产者 和 Consumers消费者
Producers生产者
Producers
将数据发布到指定的topics 主题。同时Producer
也能决定将此消息归属于哪个partition
;比如基于round-robin
方式或者通过其他的一些算法等。
Consumers消费者
本质上Kafka
只支持Topic
.每个consumer属于一个consumer group;反过来说,每个group中可以有多个consumer.发送到Topic的消息,只会被订阅此Topic的每个group中的一个consumer消费。
如果所有使用者实例具有相同的使用者组,则记录将有效地在使用者实例上进行负载平衡。
如果所有消费者实例具有不同的消费者组,则每个记录将广播到所有消费者进程。
架构和zookeeper关系
Kafka
是集群架构的,ZooKeeper
是重要组件。ZooKeeper 管理者所有的 Topic 和 Partition。
Topic 和 Partition 存储在 Node 物理节点中,ZooKeeper负责维护这些 Node,有2个 Topic,各自有2个 Partition
MQ中常见面试
怎么保证消息的可靠性
如何保证消息的顺序消费
kafka这样保证消息有序性的:
一个topic,一个 partition,一个 consumer
,内部单线程消费,单线程吞吐量太低,一般不会用这个。(全局有序性)
写 N 个内存 queue,具有相同 key的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue即可,这样就能保证顺序性。
大家可以看下消息队列的有序性是怎么推导的:
消息的有序性,就是指可以按照消息的发送顺序来消费。有些业务对消息的顺序是有要求的,比如先下单再付款,最后再完成订单,这样等。假设生产者先后产生了两条消息,分别是下单消息(M1),付款消息(M2),M1比M2先产生,如何保证M1比M2先被消费呢。
消息的有序性,就是指可以按照消息的发送顺序来消费。有些业务对消息的顺序是有要求的,比如先下单再付款,最后再完成订单,这样等。假设生产者先后产生了两条消息,分别是下单消息(M1),付款消息(M2),M1比M2先产生,如何保证M1比M2先被消费呢。
为了保证消息的顺序性,可以将将M1、M2发送到同一个Server上,当M1发送完收到ack后,M2再发送。如图:
这样还是可能会有问题,因为从MQ服务器到服务端,可能存在网络延迟,虽然M1先发送,但是它比M2晚到。
那还能怎么办才能保证消息的顺序性呢?将M1和M2发往同一个消费者,且发送M1后,等到消费端ACK成功后,才发送M2就得了。
消息队列保证顺序性整体思路就是这样啦。比如Kafka的全局有序消息,就是这种思想的体现: 就是生产者发消息时,1个Topic只能对应1个Partition,一个 Consumer,内部单线程消费。
但是这样吞吐量太低,一般保证消息局部有序即可。在发消息的时候指定Partition Key
,Kafka对其进行Hash计算,根据计算结果决定放入哪个Partition。这样Partition Key
相同的消息会放在同一个Partition。然后多消费者单线程消费指定的Partition
消息堆积如何处理
如何处理消息重复消费
重复消费问题,几乎所有类型消息队列解决重复问题都是大同小异,但是我们要搞清楚Kafka有哪些情况会导致重复消费的问题。
- 消费到收到消息了,给broker发送消息确认中,由于网络延时或者断网,broker没有收到确认,这个时候消费者会重新拉取一条消息消费,或者是consumer消费完来不及ack线程kill了。
- 消费者消费时间过长,超过了max.poll.interval.ms定义的时间,会触发Rebalance,消费者再次分配Partition后,再次poll拉取消息依然从之前消费过的消息处开始消费,这样就造成重复消费。