目录
[KafKa 相关术语](#KafKa 相关术语)
[编辑 Kafka的工作流程](#编辑 Kafka的工作流程)
[docker compose 单机部署](#docker compose 单机部署)
介绍
Apache Kafka 是分布式的、基于 发布/订阅 的容错消息系统。 主要特性如下:
-
高吞吐、低延迟:可以做到每秒百万级的吞吐量,并且时延极低
-
持久性、可靠性:消息被持久化到本地磁盘,支持数据备份防止数据丢失,具备以时间复杂度为 O(1) 的方式提供消息持久化能力;并且可以配置消息有效期,以便消费者可以多次消费
KafKa 相关术语
-
Broker: 一个 Kafka 实例就是一个 Broker ,每个主机有一个或多个 kafka 的实例(通常只有一个,所以认为一个 Kafka 主机就是一个 Broker );多个 Broker 可以组成一个集群(Cluster),其中集群内某个 Broker 会成为集群控制器(Cluster Controller),它负责管理、统筹集群
-
Topic:主题,用来存储不同类别的消息;存储消息时,需要指定存储在哪个主题下面,如发帖,发哪种类型的
-
Partition:分区,每个topic包含一个或多个partition,在创建topic时指定包含的partition数据(目的是为了进行分布式存储);分区可以提高负载(每个分区是不同的磁盘,所以会提高负载)
-
Replication:副本,每个partition分区可以有多个副本,分布在不同的Broker上。kafka会选出一个副本作为Leader,所有的读写请求都会通过Leader完成,Follower只负责备份数据;所有Follower会自动从Leader中复制数据,当Leader宕机后,会从Follower中选出一个新的Leader继续提供服务,实现故障自动转移
-
Message:消息,是通信数据的基本单位,每个消息都属于一个Partition,消息都是放在Partition里面的。消息也被称之为事件(Event)
-
Producer:消息的生产者,向kafka的一个topic发布消息,发布消息时,需要指定发布到哪个topic
-
Consumer:消息的消费者,订阅Topic并读取其发布的消息,可以订阅多个主题(类似订阅多个微信公众号)
-
Consumer Group:消费者组,每个Consumer属于一个特定的Consumer Group,多个Consumer可以属于同一个Consumer Group;各个consumer可以组成一个组,每个消息只能被组中的一个consumer消费,如果一个消息可以被多个consumer消费的话,那么这些consumer必须在不同的组。
-
ZooKeeper/Kraft:协调Kafka的正常运行,kafka将元数据信息保存在ZooKeeper/Kraft中,但发送给Topic本身的消息数据并不存储在ZK/Kraft中,而是存储在磁盘文件中。元数据信息包括:kafka有多少个节点、有哪些主题,主题叫什么,有哪些分区的等(消息自身的数据不在ZK中,而是在磁盘上对应的分区中)
Kafka的工作流程
生产者向kafka发送数据的流程
-
生产者查询Leader:producer先从zookeeper的"/brokers/.../state"节点找到该partition的leader
-
找到Leader之后往Leader写数据:producer将消息发送给该leader
-
Leader落盘:leader将消息写入本地log
-
Leader通知Follower
-
Follower从Leader中拉取数据:replication写入到Follower的本地log后,follower向leader发送ack
-
Kafka向生产者回应ACK:leader收到所有的replication的ack之后,向producer发送ack
Kafka选择分区的模式
-
直接指定往哪个分区写
-
指定key,然后kafka根据key做hash后决定写哪个分区
-
各个分区轮询
详细说明:Kafka Partition详解
Kafka选择分区的模式
-
把数据发送给Leader就认为成功,效率最高,安全性低
-
把数据发送给Leader,等待Leader回复Ack后则认为发送成功
-
把数据发送给Leader,确保Follower从Leader拉取数据回复Ack给Leader,Leader再向生产者回复Ack才认为发送成功,安全性最高
数据消费
多个消费者可以组成一个消费者组,并用一个标签来标识这个消费者组:
-
如果所有的消费者实例都在同一个消费者组中,那么消息记录会被很好的均衡发送到每个消费者实例
-
如果所有的消费者实例都在不同的消费者组,那么每一条消息记录会被广播到每一个消费者实例
各个consumer可以组成一个组,每个消息只能被组中的一个consumer消费,如果一个消息可以被多个consumer消费的话,那么这些consumer必须在不同的组
每个消费者实例可以消费多个分区,但是每一个分区最多只能被消费者组中的一个实例消费
kafka的文件存储机制
topic、partition和segment
-
在kafka文件存储中,同一个topic下有多个不同的partition:
-
每个partition就是一个目录,partition的命名规则为:topic名称+有序序号
-
第一个partition序号从0开始,序号最大值为partition数量减一
-
-
每个partition的目录下面会有多组segment文件:
-
每个partition相当于一个巨型大文件被平均分配到多个大小都相等的segment数据文件中(但每个segment file消息数量不一定相等,这种特性方便old segment file快速被删除)
-
每组segment文件包含:.index文件、.log文件、.timeindex文件(.log文件就是实际存储message的地方,.index和.timeindex文件为索引文件,用于检索消息)
-
每个partition只需要支持顺序读写就行了,segment文件生命周期由服务端配置参数决定
-
这样做能快速删除无用文件,有效提高磁盘利用率
-
-
segment文件
-
segment文件由2大部分组成,分别为index file和data file,此2个文件一一对应,成对出现,后缀".index"和".log"分别表示为segment索引文件、数据文件
-
segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充
-
存储和查找message的过程
数据写入过程
每个Partition都是一个有序并且不可改变的消息记录集合(每个partition都是一个有序队列),当新的数据写入时,就被追加到partition的末尾。
在每个partition中,每条消息都会被分配一个顺序的唯一标识,这个标识被称为Offset(偏移量),用于partition唯一标识一条消息。
数据查找过程
在partition中通过offset查找message:
-
查找segment file:每一个segment文件名都包含了上一个segment最后一条消息的offset值,所以只要根据offset二分查找文件列表,就能定位到具体segment文件
-
通过segment file查找message:当定位到segment文件后,可以通过对应的.index元数据文件,在对应的.log文件中顺序查找对应的offset,然后即可拿到数据
注意事项
-
kafka只能保证在同一个partition内部消息是有序的,在不同的partition之间,并不能保证消息有序
-
为什么kafka快:因为它把对磁盘的随机读变成了顺序读
kafka管理UI
概述
kafka的管理UI,这里主要推荐三个:
-
kafka-ui:官方文档地址为https://docs.kafka-ui.provectus.io/overview/readme。相对于EFAK,功能显得极为简陋,但基本够用,最主要是他支持kraft。另外其github上的star数量也远超EFAK
-
Kafdrop: 其github地址为GitHub - obsidiandynamics/kafdrop: Kafka Web UI。是一个 Apache 2.0 许可项目,在无数的开源选项中,Kafdrop 以其简单、快速和易于使用而脱颖而出。同时,它是一个开源 Web 项目,允许查看来自 Kafka 代理的信息,如现有主题、消费者,甚至是发送的消息内容。
-
EFAK:原名为kafka-eagle,官方文档地址为https://www.kafka-eagle.org/articles/docs/documentation.html。优点是较为完备的管理功能,相当酷炫的大盘和监控看板;缺点是当前的3.0.1版本仍然不支持Kafka的kraft部署模式,虽然官方说是支持了,但并没有给出配置说明。
更多的kafka ui可以参考: Kafka ui 搭建以及使用 - 袋鼠社区-袋鼠云丨数栈丨数据中台丨数据治理丨湖仓一体丨数据开发丨基础软件
部署
docker compose 单机部署
services:
kafka:
image: bitnami/kafka:3.8.0
container_name: kafka
restart: always
hostname: slave02
ports:
- '9092:9092'
- '9094:9094'
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://0.0.0.0:9094
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://slave02:9092,EXTERNAL://192.168.142.155:9094
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@slave02:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- ALLOW_PLAINTEXT_LISTENER=yes
- "KAFKA_HEAP_OPTS=-Xmx512m -Xms512m"
volumes:
- kafka-conf:/bitnami/kafka/config
- kafka-data:/bitnami/kafka/data
- /etc/localtime:/etc/localtime:ro
kafka-ui:
container_name: kafka-ui
image: provectuslabs/kafka-ui:latest
restart: always
ports:
- 8080:8080
environment:
DYNAMIC_CONFIG_ENABLED: true
KAFKA_CLUSTERS_0_NAME: kafka-dev
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
volumes:
- kafkaui-app:/etc/kafkaui
- /etc/localtime:/etc/localtime:ro
volumes:
kafka-conf:
kafka-data:
kafkaui-app:
启动
docker compose up -d
通过 ip + 8080 端口访问
Kafka主要配置详解
基本配置
这里主要介绍kraft部署模式下的kafka的配置文件说明。
在KRaft模式下,配置文件位于Kafk a目录中的config/kraft/server.properties
,常用配置说明如下:
-
node.id
:节点的* *id
**,一个集群中每个节点id
不能重复,需要是不小于1
的整数。类似Zookeeper的broker.id
配置。 -
controller.quorum.voters
:配置集群中Controller节点选举过程中的投票者,集群中所有的Controller节点都需要被罗列在这个配置项中 ,其配置格式为id1@host1:port1,id2@host2:port2,id3@host3:port3...
。所有的节点都是通过这个配置中的节点列表,来得知所有的控制器节点信息(以获取集群元数据)并得到投票候选者的,因此集群中所有节点,不论是Broker还是Controller,还是混合节点,都需要配置这一项。注意:这里只需要写所有的Controller节点和混合节点 的
id
、地址和端口即可,这个配置中配置的端口当然是控制器端口。 -
listeners
:这个配置项用于指定Kafka服务器监听客户端连接的地址和端口 ,当 Kafka 服务器启动时,它将监听listeners
配置项中指定的地址和端口,等待客户端的连接请求。一般情况下这个配置以PLAINTEXT://
或者CONTROLLER://
开头,意义如下:-
若这个节点是Broker节点,则以
PLAINTEXT://
开头 -
若这个节点是Controller节点,则以
CONTROLLER://
开头 -
若这个节点是混合节点,则需要同时配置两者开头的地址
下面给出几个配置示例:
-
PLAINTEXT://:9092
本节点作为Broker节点,监听本机所有可用网卡 的9092
端口,需要说明的是,该监听会监听在hostname上,也就是说如果使用默认监听,客户端需要能解析broker的hostname -
PLAINTEXT://127.0.0.1:9092
本节点作为Broker节点,监听本地的9092
端口,这样仅接受来自本地的请求 -
CONTROLLER://:10000
本节点作为Controller节点,监听本机所有可用网卡 的10000
端口(使用10000
端口作为控制器端口) -
PLAINTEXT://:9092,CONTROLLER://:9093
本节点作为混合节点,监听本机所有可用网卡 的9092
和9093
端口,其中9092
作为客户端通信端口 ,9093
作为控制器端口
-
-
advertised.listeners
:这个配置容易和listeners
混淆,事实上它们是有较大的区别的。该配置项指定Kafka服务器广播给客户端的地址和端口 ,通常配置为Kafka所在服务器的直接 提供给客户端访问的地址。当客户端(生产者或消费者)尝试连接到Kafka服务器时,它首先会获取Kafka服务器广播的地址和端口,也就是advertise.listeners
配置所指定的地址和端口,然后才会使用advertise.listeners
配置所指定的地址和端口来建立与Kafka服务器的连接。这里的问题是,既然客户端要连接Kafka,那一定是已经知道了Kafka对外的地址端口了,那为什么连接的时候还需要获取一下广播的地址端口再进行连接呢?
事实上,Kafka设计这个配置是为了解决下面较为复杂的网络场景:
-
多网络接口的主机部署 :在一个多网络接口的主机部署Kafka时,Kafka服务器可能会监听多个地址和端口,这些地址和端口可能与客户端实际访问的地址和端口不同,
advertise.listeners
允许服务器指定一个公开的、可访问的地址和端口,以便客户端能够正确连接 -
NAT/代理环境 :在某些网络环境下,Kafka服务器位于一个私有网络中,客户端位于一个公共网络中,两者之间可能存在网络地址转换(NAT)或代理,在这种情况下,Kafka服务器的内部地址和端口对客户端来说是不可访问的。通过使用
advertise.listeners
,Kafka服务器可以将一个公共地址和端口广播给客户端,使得客户端能够通过公共网络连接到服务器 -
容器环境 :例如你把Kafka放在Docker容器中运行,按照默认配置,Kafka服务端只会监听容器网络的
9092
端口,我们知道外部不能直接访问容器的网络,而是需要使用网络映射,假设你把Kafka容器的9092
端口映射至了宿主机9095
端口,也就是说外部需要通过9095
端口访问到Kafka容器的9092
端口,那么你就配置advertise.listeners
为PLAINTEXT://服务器外网地址:9095
,客户端就可以正确访问容器中的Kafka了
-
-
process.roles
这是KRaft模式下专门的配置,用于配置这个节点的类型,可以配置为下列值:-
broker
表示这个节点是Broker节点,充当消息队列的角色 -
controller
表示这个节点是Controller节点,充当元数据存放和管理的角色 -
broker,controller
表示这个节点同时担任Broker和Controller的角色,也称作混合节点
如果没有配置这个选项,则Kafka会以Zookeeper模式运行。
这里有下列注意事项:
-
如果设定节点为
controller
:-
则不能 配置
advertised.listeners
,可以将其注释掉或者删掉 -
listeners
需要配置为CONTROLLER://
开头,建议配置为CONTROLLER://:9093
-
-
如果设定节点为
broker
:-
则需要配置
advertised.listeners
为服务器外网地址和端口,这和Zookeeper模式中相同 -
listeners
需要配置为PLAINTEXT://
开头,建议配置为PLAINTEXT://:9092
-
-
如果设定节点为混合节点:
-
同样需要配置
advertised.listeners
为服务器外网地址和端口 -
listeners
需要同时配置CONTROLLER://
和PLAINTEXT://
,建议配置为PLAINTEXT://:9092,CONTROLLER://:9093
-
在开发环境或者小规模集群,可以全部使用混合节点,如果是生产环境就建议设定好每个节点的类型了!并且通常需要先启动Controller节点再启动Broker节点。
事实上,我们发现Kafka的KRaft配置目录
config/kraft
下有三个配置文件,其中server.properties
是混合节点的配置模板,而broker.properties
和controller.properties
分别是Broker节点和Controller节点的配置模板,大家如果要设定节点类型,可以直接使用对应的配置文件,将对应配置文件需要修改的部分修改一下,然后将上述格式化数据目录命令和启动命令中的配置文件路径改变一下即可,这样可以省略我们设定process.roles
和listeners
或者控制器节点删除advertise.listeners
配置的操作。 -
-
socket.send.buffer.bytes
每次发送的数据包的最大大小(单位:字节) -
socket.receive.buffer.bytes
每次接收的数据包的最大大小(单位:字节) -
socket.request.max.bytes
接收的最大请求大小(单位:字节) -
num.partitions
指定创建的Topic
的默认分区数 -
auto.create.topics.enable
:当topic不存在时,是否允许自动创建,true/false -
default.replication.factor
:指定创建的topic默认的分区的副本数,默认为1 -
logs.dir
:指定kafka的数据存储路径,多个路径可以用逗号分隔,示例:
logs.dir: /data1,/data2,/data3,/data4
-
log.retention.hours=168: 设置消息过期时间(全局)
-
log.cleanup.policy=delete: 设置过期消息的处理策略,默认为delete
内存调优
Kafka 是基于 Java 开发的,因此它的内存配置需要通过 JVM 参数进行设置。在 Kafka 的启动脚本中,有一个环境变量KAFKA_HEAP_OPTS
,可以用来设置 JVM 的内存参数。例如,可以将 KAFKA_HEAP_OPTS
设置为 -Xmx4g -Xms4g
,Kafka对堆内存的占用相对不高,一般建设6-8g就够了。
Kafka 内存配置 除了 JVM 参数之外,还需要在 Kafka 的配置文件中配置内存。具体来说,需要关注以下两个参数:
-
log.retention.bytes:用于设置 Kafka 存储消息的阈值,当日志文件大小达到这个阈值时,Kafka 会删除最旧的消息。因此,这个参数应该根据存储需求和可用内存进行
-
log.segment.bytes:这个参数设置了 Kafka 日志文件的大小。如果设置得太小,会导致频繁的文件切换,增加文件系统的开销;如果设置得太大,会占用过多的内存。因此,这个参数也需要根据存储需求和可用内存进行调整