这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。
谈到消息队列,大家都知道异步、削峰、解藕,并且还要他和普通队列不同,能做到高吞吐、高并发、高可用。Kafka 作为一个最经典的消息队列中间价,将是这篇笔记的主角。
本文目录
- Kafka 简介及其大体架构
- Kafka 是如何提高吞吐量及稳定性的
- 2.1 Producer: 批量发送、数据压缩
- 2.2 Broker: 顺序写、消息索引、零拷贝
- 2.3 Consumer: Rebalance
- Kafka 还存在什么问题
Kafka 简介及其大体架构
Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。该项目的目标是为处理实时数据提供一个统一、高吞吐、低延迟的平台。其持久化层本质上是一个“按照分布式事务日志架构的大规模发布/订阅消息队列”,这使它作为企业级基础设施来处理流式数据非常有价值。 ——来自维基百科
其大致架构如下靓图所示, 逻辑上: 物理上:
其中一些概念解释如下:
- Topic 即逻辑上的队列,用户在使用时根据不同主题建立的不同队列
- Partition 是 Topic 物理上的分组,一个 Topic 可以分成多个 Partition
- Cluster 是物理集群,其中包含多个 Broker
- Broker 是用来存储消息的,Cluster 中每一个 服务器就是一个 Broker
- Producer 是生产者,将业务消息发送到 Topic 中
- Consumer 是消费者,负责消费 Topic 中的消息
- Consumer 是消费者组,不同组之间的消费无互不干涉
在使用上,一般的步骤是:
- 搭建 Kafka 集群
- 创建 Topic
- 编写生产者逻辑
- 编写消费者逻辑
另外,Kafka 中还有一些比较重要的基础概念。
Offset
Offset 指消息在 partition 内的相对位置信息,可以理解为唯一ID,在 partition 内严格递增。
Replica
每个分片(Partition)有多个 Replica,分布在不同的机器上以保证 Kafka 的高可用。其中 Leader Replica 会从 ISR 中选出。
- Leader Replica:是对外服务的副本,其他 Follower 会来该副本上拉取最新数据以保证消息同步。
- ISR:该集合内只允许存在与 Leader 差距不大的 Follower,当 Leader 挂掉时只允许从该集合内的 Follower 中选择新 Leader。
Kafka 是如何提高吞吐量及稳定性的
首先我们要知道一条消息是由 Producer 生产,发送给 Broker,再由 Consumer 消费。在这三个环节 Kafka 都有对应的优化机制。
Producer: 批量发送、数据压缩
对于 Producer 而言,如果一条一条发送消息,将会产生大量的 IO 消耗,因此最直接的方案就是采用批量发送,减少IO次数。
而若消息量很大,可能会导致网络带宽不够用,于是 Kafka 便采用数据压缩的方式减少消息大小。
Broker: 顺序写、消息索引、零拷贝
Broker 要将消息存储到磁盘中,如何做才能提高性能?
考虑到磁盘的结构,我们需要移动磁头找到对应磁道,磁盘转动,然后找到对应扇区,最后才进行写入。这过程中寻道成本比较高并且可优化,只要使用顺序写的方式就可以减少寻道带来的时间成本。
在 Consumer 请求消息数据时,怎样才能尽快找到数据呢?
Kafka 会有一个偏移量索引文件,当消费者需要寻找基于某个 offset 的消息时,使用二分查找找到小与目标 offset 的最大文件,再顺序找到需要的文件。同样的,它还有一个时间戳索引文件,搜索方式也差不多,这里不再赘述。
在取出数据时,需要将数据从磁盘空间拷贝到内核空间,而这其中会涉及到多次拷贝,如图所示。
Kafka 采用零拷贝拘束,减少了这些额外的拷贝次数以提高性能。
Consumer: Rebalance
对于一个消费者组而言,同一个 Topic 下多个 Partition 如何分配给底下的消费者呢?
如果采取硬编码方式,将某些 Partition 分配给某个消费者,其他的分配给另一个消费者,当某个消费者进程挂了,那么它负责的 Partition 需要重新手动分配。又或者新加入了一个消费者,那要手动去除其他消费者负责的 Partition 来将这些任务让给新消费者。
因此可以采用 Rebalance 的方式,对于每个消费者组,在 Broker 中选取一台作为 Coordinator 帮助他们进行 Partition 的动态分配,整个 Rebalance 的方式如下图所示。
Kafka 还存在什么问题
- 运维成本高
- 对于负载不均衡的场景,解决方案复杂
- 没有自己的缓存,完全依赖 Page Cache
- Controller、Coordinator 和 Broker 在同一进程中,大量 IO 会造成其性能下降