Kafka是是一个优秀的分布式消息中间件,关于常用的消息中间件对比可参考文章:消息中间件概述。
Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多生产者、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。
Kafka主要设计目标如下:
有两种主要的消息传递模式:点对点传递模式、发布-订阅模式。大部分的消息系统选用发布-订阅模式。Kafka就是一种发布-订阅模式。
对于消息中间件,消息分推拉两种模式。Kafka只有消息的拉取,没有推送,可以通过轮询实现消息的推送。
Kafka具有四个核心API:
日志收集: 一个公司可以用Kafka可以收集各种服务的Log,通过Kafka以统一接口服务的方式开放给各种Consumer;
消息系统: 解耦生产者和消费者、缓存消息等;
用户活动跟踪: Kafka经常被用来记录Web用户或者App用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到Kafka的Topic中,然后消费者通过订阅这些Topic来做实时的监控分析,亦可保存到数据库;
运营指标: Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告;
流式处理: 比如Spark Streaming和Storm。
kafka的整体架构如下图所示
图片来源:https://mp.weixin.qq.com/s/_g11mmmQse6KrkUE8x4abQ
从大的组件上看,kafka主要由Producer、Broker、Consumer 以及负责集群管理的 ZooKeeper 组成。除了极大组件,整个消息的流转还涉及到还有几个特别重要的概念—主题(Topic)、分区(Partition)、分段(segment)、位移(offset)。
先来整体了解下基本的架构流程。
下面来具体看下各个组件和概念。
生产者,负责消息的创建并通过一定的路由策略发送消息到合适的 Broker。broker将该消息追加到当前用于追加数据的 segment 文件中。
一般情况下,一个消息会被发布到一个特定的主题(Topic)上。
消费者,负责从 Broker 中拉取(Pull)订阅的消息并进行消费,通常多个消费者(Consumer)构成一个分组(Consumer Group),消费者组是为了保证同一个消息只能被一个组里一个消费者消费,同时可以动态扩容。
具有如下特点:
消费者与消费组这种模型可以让整体的消费能力具备横向伸缩性,我们可以增加(或减少) 消费者的个数来提高(或降低)整体的消费能力。对于分区数固定的情况,一味地增加消费者 并不会让消费能力一直得到提升,如果消费者过多,出现了消费者的个数大于分区个数的情况, 就会有消费者分配不到任何分区。
服务实例,负责消息的持久化、中转等功能。一个独立的Kafka 服务器被就是一个broker。
broker 是集群的组成部分。每个集群都有一个broker 同时充当了集群控制器Controller的角色。在每一个Broker在启动时都会像向ZK注册信息,ZK会选取一个最早注册的Broker作为Controller,后面Controller会与ZK进行数据交互获取元数据(即整个Kafka集群的信息,例如有那些Broker,每个Broker中有那些Partition等信息),并负责管理工作,包括将分区分配给broker 和监控broker,其他Broker是与Controller进行交互进而感知到整个集群的所有信息。
在集群中,一个分区从属于一个broker,该分区被称为分区的首领Leader。一个Topic的不同分区一般是分布在不同的broker中。
broker 可以为消费者提供服务,对读取分区的请求作出响应,返回已经提交到磁盘上的消息。
负责 broker、consumer 集群元数据的管理等;
(注意:Producer 端直接连接 broker,不在 zk 上存任何数据,只是通过 ZK 监听 broker 和 topic 等信息)
每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。
Kafka 按 topic 对消息进行分类,我们在收发消息时只需指定 topic。
的Topic是逻辑概念,并没有物理存在,但是注意物理上不同Topic的消息是分开存储的。
主题就好比数据库的表,尤其是分库分表之后的逻辑表。
分区。为了提升系统的吞吐,一个 topic 下通常有多个 partition,partition 分布在不同的 Broker 上,用于存储 topic 的消息,这使 Kafka 可以在多台机器上处理、存储消息,给 kafka 提供给了并行的消息处理能力和横向扩容能力。另外,为了提升系统的可靠性,partition 通常会分组,且每组有一个主 partition、多个副本 partition,且分布在不同的 broker 上,从而起到容灾的作用。
下面是分区数量和集群数量不同情况时,分区的分布情况:
消息以追加的方式写入分区,然后以先入先出的顺序读取。
副本。即分区的副本。上面我们说到为了提升系统的可靠性,partition 通常会分组,且每组有一个主 partition、多个副本 partition,且分布在不同的 broker 上,从而起到容灾的作用。
图中每个TopicA-x都是一个Partition,其中后面的数字代表了一个分区中的第几个副本,同一个分区的副本需要分布在不同的broker中。
此外,我们说过多个副本Partition中会选取一个作为leader,其他的作为follower。生产者在发送数据的时候,是直接发送到leader partition里面,然后follower partition会去leader那里自行同步数据,消费者消费数据的时候,也是从leader那去消费数据的。
副本处于不同的 broker 中,当 leader 副本出现故障时,从 follower 副本中重新选举新的 leader 副本对外提供服务。Kafka 通过多副本机制实现了故障的自动转移,当 Kafka 集群中某个 broker 失效时仍然能保证服务可用。
关于如何确定副本故障和故障转移的,后面还会详细介绍。
偏移量。有两个:生产者偏移量(也是消息存储的偏移量)和消费者偏移量。
即消息在日志中的位置。消息写入的时候,每一个分区都有一个offset,这个offset就是生产者的offset,同时也是这个分区的最新最大的offset。offset 是消息在分区中的唯一标识,是一个单调递增且不变的值。
有些时候没有指定某一个分区的offset,这个工作kafka帮我们完成。
Kafka 通过它来保证消息在分区内的顺序性,不过 offset 并不跨越分区,所以,Kafka 保证的是分区有序而不是主题有序。但是在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1。
当消费者进行消费的时候,是要去指分区找消息的Offset,从而找到消息进行消费。他与分区的最新消息的Offset是不一样的。他是存储在消费者组中,每个分区都有一个Offset,用于标识,当前消费者组下一个要消费消息的Offset。
如上图,这是某一个分区的offset情况,生产者写入的offset是最新最大的值是12,而当Consumer A进行消费时,从0开始消费,一直消费到了9,消费者的offset就记录在9,Consumer B就纪录在了11。等下一次他们再来消费时,他们可以选择接着上一次的位置消费,当然也可以选择从头消费,或者跳到最近的记录并从“现在”开始消费。
分段。宏观上看,一个 partition 对应一个日志(Log)。由于生产者生产的消息会不断追加到 log 文件末尾,为防止 log 文件过大导致数据检索效率低下,Kafka 采取了分段和索引机制,将每个 partition 分为多个 segment,同时也便于消息的维护和清理。每个 segment 包含一个.log 日志文件、两个索引(.index、timeindex)文件以及其他可能的文件。每个 Segment 的数据文件以该段中最小的 offset 为文件名,当查找 offset 的 Message 的时候,通过二分查找快找到 Message 所处于的 Segment 中。
参考:
https://mp.weixin.qq.com/s/_g11mmmQse6KrkUE8x4abQ
https://mp.weixin.qq.com/s/v6jUK8TIPi1Debfd40GU3w