文章目录
1. Kafka 概述
1.1. 消息队列
消息队列内部实现原理

- 点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
点对点模型通常是一个基于拉取或者轮询的消息传送模型,这种模型从队列中请求信息,
而不是将消息推送到客户端。这个模型的特点是发送到队列的消息被一个且只有一个接收者 接收处理,即使有多个消息监听者也是如此。
- 发布/订阅模式(一对多,数据生产后,推送给所有订阅者)
发布订阅模型则是一个基于推送的消息传送模型。发布订阅模型可以有多种不同的订阅
者,临时订阅者只在主动监听主题时才接收消息,而持久订阅者则监听主题的所有消息,即 使当前订阅者不可用,处于离线状态。
1.2 为什么需要消息队列(优点)
- 解耦
允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
- 冗余
消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
- 扩展性
因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
- 峰值处理能力
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。 如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列 能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
- 可恢复性
系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
- 顺序保证
在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(Kafka 保证一个 Partition 内的消息的有序性)
- 缓冲
有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
- 异步通信
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
1.3 什么是 Kafka
在流式计算中,Kafka 一般用来缓存数据,Storm/Flink通过消费 Kafka 的数据进行计算。
- Apache Kafka 是一个开源消息系统,由 Scala 写成。是由 Apache 软件基金会开发的 一个开源消息系统项目。
- Kafka 最初是由 LinkedIn 公司开发,并于 2011 年初开源。2012 年 10 月从 Apache Incubator 毕业。该项目的目标是为处理实时数据提供一个统一、高通量、低等待的平台。
- Kafka 是一个分布式消息队列。Kafka 对消息保存时根据 Topic 进行归类,发送消息者称为 Producer,消息接受者称为 Consumer,此外 kafka 集群有多个 kafka 实例组成,每个实例(server)称为 broker。
- 无论是kafka 集群,还是 consumer 都依赖于 zookeeper 集群保存一些 meta 信息,来保证系统可用性。
1.4 Kafka 架构
Kafka 整体架构图

Kafka 详细架构图

- Producer :
消息生产者,就是向 kafka broker 发消息的客户端
- Consumer :
消息消费者,向 kafka broker 取消息的客户端
- Topic :
可以理解为一个队列
- Consumer Group(CG):
这是 kafka 用来实现一个 topic 消息的广播(发给所有的 consumer) 和单播(发给任意一个 consumer)的手段。一个 topic 可以有多个 CG。topic 的消息会复制 (不是真的复制,是概念上的)到所有的 CG,但每个 partion 只会把消息发给该 CG 中的一 个 consumer。如果需要实现广播,只要每个 consumer 有一个独立的 CG 就可以了。要实现 单播只要所有的 consumer 在同一个 CG。用 CG 还可以将 consumer 进行自由的分组而不需 要多次发送消息到不同的 topic;
- Broker :
一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic;
- Partition:
为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上, 一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。partition 中的每条消息 都会被分配一个有序的 id(offset)。kafka 只保证按一个 partition 中的顺序将消息发给 consumer,不保证一个 topic 的整体(多个 partition 间)的顺序;
- Offset:
kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查 找。例如你想找位于 2049 的位置,只要找到 2048.kafka 的文件即可。当然 the first offset 就 是 00000000000.kafka。
2. Kafka集群部署
2.1. 环境准备
2.1.1. 集群规划
| hadoop102 | hadoop103 | hadoop104 |
|---|---|---|
| zk | zk | zk |
| kafka | kafka | kafka |
2.1.2. jar 包下载
http://kafka.apache.org/downloads.html
2.2. Kafka 集群部署
- 解压安装包
tar -zxvf kafka_2.11-0.11.0.0.tgz -C /opt/software/
- 修改解压后的文件名称
mv kafka_2.11-0.11.0.0 kafka
- 在/opt/software/kafka 目录下创建 logs 文件夹
mkdir logs
- 修改配置文件 输入以下内
cd config/
vim server.properties
输入以下内容:
#broker 的全局唯一编号,不能重复
broker.id=0
#删除 topic 功能使能
delete.topic.enable=true
#处理网络请求的线程数
num.network.threads=3
#用来处理磁盘 IO 的现成数
num.io.threads=8
#发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
#接收套接字的缓冲区大小
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小
socket.request.max.bytes=104857600
#kafka 运行日志存放的路径
log.dirs=/opt/module/kafka/logs
#topic 在当前 broker 上的分区个数
num.partitions=1
#用来恢复和清理 data 下数据的线程数量
num.recovery.threads.per.data.dir=1
#segment 文件保留的最长时间,超时将被删除
log.retention.hours=168
#配置连接 Zookeeper 集群地址
zookeeper.connect=hadoop102:2181,hadoop103:2181,hadoop104:2181
- 配置环境变量
sudo vi /etc/profile
#KAFKA_HOME
export KAFKA_HOME=/opt/module/kafka export PATH=$PATH:$KAFKA_HOME/bin
source /etc/profile
- 分发安装包
xsync kafka/
注意:分发之后记得配置其他机器的环境变量
-
分别在 hadoop103 和 hadoop104 上修改配置文件/opt/software/kafka/config/server.properties 中的 broker.id=1、broker.id=2
注:broker.id 不得重复 -
启动集群
依次在 hadoop102、hadoop103、hadoop104 节点上启动 kafka
bin/kafka-server-start.sh config/server.properties &
- 关闭集群
bin/kafka-server-stop.sh stop
2.3. Kafka 命令行操作
2.3.1. 查看当前服务器中的所有 topic
bin/kafka-topics.sh --zookeeper hadoop102:2181 --list
2.3.2. 创建 topic
bin/kafka-topics.sh --zookeeper hadoop102:2181 \ --create --replication-factor 3 --partitions 1 --topic first
选项说明:
- topic 定义 topic 名
- replication-factor 定义副本数
- partitions 定义分区数
2.3.3. 删除 topic
bin/kafka-topics.sh --zookeeper hadoop102:2181 \ --delete --topic first
2.3.4. 发送消息
bin/kafka-console-producer.sh \ --broker-list hadoop102:9092 --topic first
>hello world
>atguigu atguigu
2.3.5. 消费消息
bin/kafka-console-consumer.sh \ --zookeeper hadoop102:2181 --from-beginning --topic first
- from-beginning:会把 first 主题中以往所有的数据都读取出来。根据业务场景选择是否 增加该配置。
2.3.6. 查看某个 Topic 的详情
bin/kafka-topics.sh --zookeeper hadoop102:2181 \ --describe --topic first
- Kafka 工作流程
3.1. Kafka 生产过程分析
3.1.1. 写入方式
producer 采用推(push)模式将消息发布到 broker,每条消息都被追加(append)到分 区(patition)中,属于顺序写磁盘(顺序写磁盘效率比随机写内存要高,保障 kafka 吞吐率)。
3.1.2 分区(Partition)
消息发送时都被发送到一个 topic,其本质就是一个目录,而 topic 是由一些 Partition Logs(分区日志)组成,其组织结构如下图所示:

我们可以看到,每个 Partition 中的消息都是有序的,生产的消息被不断追加到 Partition log 上,其中的每一个消息都被赋予了一个唯一的 offset 值。
- 分区的原因
- 方便在集群中扩展,每个 Partition 可以通过调整以适应它所在的机器,而一个 topic 又可以有多个 Partition 组成,因此整个集群就可以适应任意大小的数据了
- 可以高并发,因为可以以 Partition 为单位读写了
- 分区的原则
- 指定了 patition,则直接使用
- 未指定 patition 但指定 key,通过对 key 的 value 进行 hash 出一个 patition
- patition 和 key 都未指定,使用轮询选出一个 patition
3.1.3. 副本(Replication)
同一个 partition 可能会有多个 replication(对应 server.properties 配置中的 default.replication.factor=N)。没有 replication 的情况下,一旦 broker 宕机,其上所有 patition 的数据都不可被消费,同时 producer 也不能再将数据存于其上的 patition。引入 replication 之 后,同一个 partition 可能会有多个 replication,而这时需要在这些 replication 之间选出一个 leader,producer 和 consumer 只与这个 leader 交互,其它 replication 作为 follower 从 leader 中复制数据。
3.1.4. 写入流程

producer 写入消息流程如下:
- producer 先从 zookeeper 的 "/brokers/…/state"节点找到该 partition 的 leader
- producer 将消息发送给该 leader
- leader 将消息写入本地 log
- followers 从 leader pull 消息,写入本地 log 后向 leader 发送 ACK
- leader 收到所有 ISR 中的 replication 的 ACK 后,增加 HW(high watermark,最后 commit 的 offset)并向 producer 发送 ACK
producer可以设置的ACK的值有3个:
- 0:表示不需要确认Kafka是否收到数据,只管发
- 1:表示只需要leader确认后,就发下一条数据
- all:表示需要leader/follower都确认后再发下一条数据
3.2. Broker 保存消息
3.2.1. 存储方式
物理上把topic分成一个或多个patition(对应 server.properties 中的num.partitions=3配置),每个 patition 物理上对应一个文件夹(该文件夹存储该 patition 的所有消息和索引文 件)
3.2.2. 存储策略
无论消息是否被消费,kafka 都会保留所有消息。有两种策略可以删除旧数据:
- 基于时间:log.retention.hours=168
- 基于大小:log.retention.bytes=1073741824
需要注意的是,因为 Kafka 读取特定消息的时间复杂度为 O(1),即与文件大小无关,所以这里删除过期文件与提高 Kafka 性能无关。
3.2.3. Zookeeper 存储结构

3.3. Kafka 消费过程分析
kafka 提供了两套 consumer API:高级 Consumer API 和低级 Consumer API。
3.3.1 高级 API
- 高级 API 优点
- 高级 API 写起来简单
- 不需要自行去管理 offset,系统通过 zookeeper 自行管理。
- 不需要管理分区,副本等情况,.系统自动管理。
- 消费者断线会自动根据上一次记录在 zookeeper 中的 offset 去接着获取数据(默认设置1 分钟更新一下 zookeeper 中存的 offset)
可以使用 group 来区分对同一个 topic 的不同程序访问分离开来(不同的 group 记录不同的 offset,这样不同程序读取同一个 topic 才不会因为 offset 互相影响)
- 高级 API 缺点
- 不能自行控制 offset(对于某些特殊需求来说) 不能细化控制如分区、副本、zk 等
3.3.2. 低级 API
- 低级 API 优点
- 能够让开发者自己控制 offset,想从哪里读取就从哪里读取。
- 自行控制连接分区,对分区自定义进行负载均衡
- 对 zookeeper 的依赖性降低(如:offset 不一定非要靠 zk 存储,自行存储 offset 即可,比如存在文件或者内存中)
- 低级 API 缺点
- 太过复杂,需要自行控制 offset,连接哪个分区,找到分区 leader 等。
3.3.3 消费者组

消费者是以 consumer group 消费者组的方式工作,由一个或者多个消费者组成一个组, 共同消费一个 topic。每个分区在同一时间只能由 group 中的一个消费者读取,但是多个 group 可以同时消费这个 partition。在图中,有一个由三个消费者组成的 group,有一个消费者读 取主题中的两个分区,另外两个分别读取一个分区。某个消费者读取某个分区,也可以叫做 某个消费者是某个分区的拥有者。
在这种情况下,消费者可以通过水平扩展的方式同时读取大量的消息。另外,如果一个 消费者失败了,那么其他的 group 成员会自动负载均衡读取之前失败的消费者读取的分区。
3.3.4. 消费方式
consumer 采用 pull(拉)模式从 broker 中读取数据。
push(推)模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成 consumer 来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而 pull 模式则可以根据 consumer 的消费能力以适当的速率消费消息。
对于 Kafka 而言,pull 模式更合适,它可简化 broker 的设计,consumer 可自主控制消费消息的速率,同时 consumer 可以自己控制消费方式——即可批量消费也可逐条消费,同时 还能选择不同的提交方式从而实现不同的传输语义。
pull 模式不足之处是,如果 kafka 没有数据,消费者可能会陷入循环中,一直等待数据 到达。为了避免这种情况,我们在我们的拉请求中有参数,允许消费者请求在等待数据到达 的“长轮询”中进行阻塞(并且可选地等待到给定的字节数,以确保大的传输大小)。
4. Kafka拦截器
5. Kafka 配置信息
5.1. Broker 配置信息
| 属性 | 值 | 描述 |
|---|---|---|
| broker.id | - | 必填参数,broker 的唯一标识 |
| log.dirs | /tmp/ka fka-logs | Kafka 数据存放的目录。可以指定多个目录, 中间用逗号分隔,当新 partition 被创建的时 会被存放到当前存放 partition 最少的目录。 |
| port | 9092 | BrokerServer 接受客户端连接的端口号 |
| zookeeper.connect | null | Zookeeper 的连接串,格式为:hostname1:port1,hostname2:port2,hostname3:port3。可以填一个或多个,为了提高可靠性,建议都填上。注意,此配置允许我们指定一个 zookeeper 路径来存放此 kafka 集群的所有数据,为了与其他应用集群区分开,建 议在此配置中指定本集群存放目录,格式为:hostname1:port1,hostname2:port2,hostname3:port3/chroot/path 。需要注意的是, 消费者的参数要和此参数一致。 |
| message.max.bytes | 1000000 | 服务器可以接收到的最大的消息大小。注意此参数要和 consumer的maximum.message.size 大小一致,否则会因为生产者生产的消息太大导致消费者无法消费 |
| num.io.threads | 8 | 服务器用来执行读写请求的 IO 线程数,此参 数的数量至少要等于服务器上磁盘的数量 |
| queued.max.request | 500 | I/O 线程可以处理请求的队列大小,若实际请求数超过此大小,网络线程将停止接收新的请求。 |
| socket.send.buffer.bytes | 100 * 1024 | The SO_SNDBUFF buffer the server prefers for socket connections. |
本文深入解析Kafka消息队列的基本概念、架构特点及部署流程。覆盖消息队列的优势、Kafka的工作原理、集群搭建步骤、命令行操作指南等内容。
501

被折叠的 条评论
为什么被折叠?



