目录
一. KafKa概述
1.1 什么是kfk
kfk是一个分布式
的基于发布订阅模式
的消息队列
. 主要应用于大数据实时处理领域.
1.2 消息队列的两种模式
消息队列有两种模式: 点对点模式 和 发布订阅模式. kfk是基于发布订阅模式的消息队列.
- 点对点模式(一对一): 消费者主动拉取消息, 消息收到后删除消息
生产者生产消息发送到queue中, 然后消费者从queue中主动拉取
数据并消费.
消息被消费后从queue中删除
, 所以消费者不可能消费到已经被消费的消息.
queue支持存在多个消费者, 但是对一个消息而言, 只能被一个消费者消费.
- 发布订阅模式(一对多):
生产者将消息发布到topic, 多个消费者订阅该topic. 发布到topic的消息会被所有的的订阅者消费.
topic作用是进行消息分类, 例如订单相关消息放到订单topic, 用户相关消息放到用户topic.
订阅topic类似订阅微信公众号.
-
发布订阅模式下又分为两种模式:
消息队列主动向消费者推送消息
和消费者主动从队列中拉取消息
.
kafka属于发布订阅模式中的消费者主动从队列中拉取消息的模式
. -
发布订阅模式中消费者主动拉取数据的模式的优点:
- 同样的消息可以发送给多个消费者(这是发布订阅模式的优点).
消费者消费数据的速度可以让消费者自己决定
(如果是队列主动推送消息到消费者, 假如队列
每秒推送50M数据, 消费者每秒消费10M数据, 那么消费者就崩了, 每个消费者的消费能力是不
同的).
-
发布订阅模式中消费者主动拉取数据的模式的缺点:
有时候会很浪费资源
(消费者主动拉取消息, 那么消费者必须一直轮询队列判断是否有新消息,
即便是队列中没有新消息时也要寻轮询问队列是否有消息. 即维护一个长轮询, 不断询问是否
有新消息).
-
注意: 根据上面两点可以知道点对点模式的优缺点.
-
注意: "发布订阅模式中消费者主动拉取数据的模式"中, 缺点是需要消费者维护一个长轮询, 不断询
问mq是否有新消息. 那么能不能mq中收到消息之后通知消费者, 这时让消费者再主动拉取呢? 可以,
但是这样也是有缺点的. 优点就是消费者不用维护和mq的长轮询了. 缺点就是mq需要维护一个列
表, 这个列表中放的是所有要订阅这个topic的消费者. 还有一个缺点是如果消费者挂了, mq就通知
不到了. -
注意: 消息队列并不是文件存储系统,
所以消息队列保存数据是有期限的
, 不能无限期保存数据, 期限可配置.
kfk中数据默认保存168小时, 即7天. kafka存消息存到磁盘中, 即能持久化.
-
kfk的日志清理策略:
- kfk有3种日志清理策略: ①基于时间 ②基于日志大小 ③基于日志起始偏移量
- 基于时间可以设置日志保留的小时、分钟、毫秒,毫秒的优先级最高,小时的优先级最低
- 基于日志大小默认1G
- 默认基于时间,168小时(7天)后删除
- kfk有3种日志清理策略: ①基于时间 ②基于日志大小 ③基于日志起始偏移量
1.3 kfk的架构
-
每个broker就是一个kfk服务, 就是一个kfk进程. 多个broker构成一个kfk集群. 一个broker可以容纳多个topic.
-
topic作用是消息分类. 例如订单相关消息存到订单topic, 用户相关消息存到用户topic.
-
一个topic可以分为多个
partition 分区
, 一个topic可以分布到多个broker中.
分区的作用是提高topic的负载能力, 可以说是提高读写并发度. -
replica 副本
. 为了保证broker发生故障时, 该节点中的partition数据不会丢失且kfk能继续工作, kfk提供了副本机制, 一个topic的每个partition都可以有多个replica. 一个leader和若干个follower, 说一个partition有3个replica, 说明这个partition有1个leader和2个follower.
分区的副本的作用是做高可用. lader挂掉会将follower提升为leader. 同一个partition的leader和follower肯定不能在同一个broker(kafka服务器)中, 否则不能高可用. 无论是生产者还是消费者, 无论是发送消息还是接受消息, 只会找leader, follower只是起到备份作用. -
consumer group 消费者组(CG)
. 由多个consumer组成. 消费者组中每个消费者负责消费不同partition的数据, 一个partition只能由一个组内的消费者消费. 消费者组之间互不影响. 消费者肯定属于某个消费者组. 消费者组是一个逻辑上的订阅者.这里面有一个消费者组中的分区分配策略问题. 生产者也有一个消息发送到分区的分配策略问题.
某一个partition分区中的数据只能被同一个消费者组中的某一台消费者消费. 但是可以有多个消费者组中的一台消费者消费同一个partition分区中的数据. 我们可以将同一个组的所有消费者当做一个大的消费者.
消费者组的作用是提高消费者的消费能力. 假如一个topic两个partition, 每个partition中各有50条数据, 只有一台消费者, 那么一台消费者需要消费50条数据. 假如一个topic两个partition, 每个partition中有50条数据, 两台消费者, 那么每台消费者可以消费50条数据, 速度快了一倍. 两台消费者必须同一个组(消费者集群中的所有节点作为一个mq消费者组), 因为两个partition属于同一个topic. 消费者组中消费者的个数多于topic的partition分区数没有意义, 最好消费者组中的消费者个数和topic中的partition分区数相同.
-
zk在kfk中的作用: 帮kafka集群存信息, 帮消费者存储消费数据的位置偏移量offset(看kfk版本).
zk帮助我们管理kafka集群. 如果想使多台kafka作为一个集群, 只需要让这多台kafka使用同一个zk即可, 当然zk可以是集群.
消费者也要向zk中存储一些数据. 比如一个partition里面10条消息, 一台消费者消费到5条的时候挂了, 那么消费者重启需要从第6条消息开始消费, 消费者消费数据的位置信息就需要存到zk中. 肯定不能只 保存在消费者的内存中啊, 消费者挂了就操蛋了. 消费者内存中也是有一份消费数据的位置信息(offset偏移量)的.
注意, kafka0.9版本前, 消费者消费消息的offset存在zk中; kafka0.9版本及之后, 消费者消费消息的offset存在kafka中, 存在kafka内置的topic中; 无论消费者消费消息的offset存在哪里, 作用都是记录消费位置, 以便当消费者挂掉后重启后可以接着消费消息.
为什么要改呢, 肯定是存在zk中不好. 消费者和kfk之间维持连接, 还要和zk之间维持连接, 消费者拉取消息是很快的, 那么就需要消费者频繁和zk交互, 为了避免这种情况就改了.
二. kfk入门
2.1 安装部署kfk
(可以参考:https://www.cnblogs.com/malcolmfeng/p/13372081.html )
# 解压即可使用
[root@king kafka]# tar -axvf kafka_2.11-0.11.0.0.tgz
# 重命名
[root@king kafka]# mv kafka_2.11-0.11.0.0 kafka
[root@king kafka]# pwd
/opt/kafka/kafka
[root@king kafka]# ll
total 52
drwxr-xr-x 3 root root 4096 Jun 23 2017 bin
drwxr-xr-x 2 root root 4096 Jun 23 2017 config
drwxr-xr-x 2 root root 4096 Nov 22 23:18 libs
-rw-r--r-- 1 root root 28824 Jun 23 2017 LICENSE
-rw-r--r-- 1 root root 336 Jun 23 2017 NOTICE
drwxr-xr-x 2 root root 4096 Jun 23 2017 site-docs
[root@king config]# pwd
/opt/kafka/kafka/config
[root@king config]# ll
total 64
-rw-r--r-- 1 root root 906 Jun 23 2017 connect-console-sink.properties
-rw-r--r-- 1 root root 909 Jun 23 2017 connect-console-source.properties
-rw-r--r-- 1 root root 5807 Jun 23 2017 connect-distributed.properties
-rw-r--r-- 1 root root 883 Jun 23 2017 connect-file-sink.properties
-rw-r--r-- 1 root root 881 Jun 23 2017 connect-file-source.properties
-rw-r--r-- 1 root root 1111 Jun 23 2017 connect-log4j.properties
-rw-r--r-- 1 root root 2730 Jun 23 2017 connect-standalone.properties
-rw-r--r-- 1 root root 1199 Jun 23 2017 consumer.properties
-rw-r--r-- 1 root root 4696 Jun 23 2017 log4j.properties
-rw-r--r-- 1 root root 1900 Jun 23 2017 producer.properties
-rw-r--r-- 1 root root 6954 Jun 23 2017 server.properties
-rw-r--r-- 1 root root 1032 Jun 23 2017 tools-log4j.properties
-rw-r--r-- 1 root root 1023 Jun 23 2017 zookeeper.properties
# 重点的配置文件:
server.properties
zookeeper.properties
-
/opt/kafka/kafka/config/server.properties
############################# Server Basics ############################# # kfk服务器的id, 每台kfk都要配置唯一的整数 broker.id=0 # 设置是否可以删除topic, 默认注释掉, 默认false. 一般我们将其改为true, 打开注释即可. delete.topic.enable=true ############################# Socket Server Settings ############################# # kfk默认端口9092, 在此修改为9091 listeners=PLAINTEXT://:9091 # 当前的kfk可以被外界访问. PLAINTEXT://当前kfk的ip:当前kfk的端口 advertised.listeners=PLAINTEXT://47.111.177.219:9091 ############################# Log Basics ############################# # kfk存储数据使用的目录. kfk的数据和日志都存在这个目录中. # kfk存储数据的文件的后缀名叫.log, kfk日志文件的后缀名也叫.log # 我们是有办法将日志和数据存放的目录区分开的, 需要额外的配置. log.dirs=/tmp/kafka-logs ############################# Log Retention Policy ############################# # kfk中的数据(生产者发来的消息)默认保存到磁盘7天 log.retention.hours=168 # 存储kfk数据的一个文件的大小, 1G log.segment.bytes=1073741824 ############################# Zookeeper ############################# # zk, 如果是zk集群那么多个节点配置按照"127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002". zookeeper.connect=localhost:2181
完成上述配置,
先启动zk, 再自动kfk
. -
kfk的启动命令
/opt/kafka/kafka/bin
-
kfk启动/停止 单台节点
# 启动kfk. 不加-daemon不能后台启动 /opt/kafka/kafka/bin/kafka-server-start.sh -daemon /opt/kafka/kafka/config/server-1.properties /opt/kafka/kafka/bin/kafka-server-start.sh -daemon /opt/kafka/kafka/config/server-2.properties /opt/kafka/kafka/bin/kafka-server-start.sh -daemon /opt/kafka/kafka/config/server-3.properties # 停止kfk /opt/kafka/kfk/bin/kafka-server-stop.sh
可以编写shell脚本群启kfk.
三台broker只要连接同一个zk集群, 就是同一个kfk集群中的节点, 注意三台broker中的broker.id
不能相同.# linux检查端口占用 netstat -unltp | grep 909*
2.2 kfk的命令行操作
2.2.1 命令行操作topic
-
创建topic并指定分区和副本
/bin/kafka-topics.sh --create --zookeeper localhost:2181 --topic topic01 --partitions 2 --replication-factor 2
创建topic. 使用
--create
参数. 需要指定zk地址.--topic topic01
# 主题的名字. 主题的作用是帮我们分类消息.
--partitions 2
# 两个分区.
--replication-factor 2
# 每个分区有两个副本.创建同名topic将报错;
-
查看所有的topic
/bin/kafka-topics.sh --list --zookeeper localhost:2181
查看topic将打印topic的名字.
-
kfk集群和消费者需要向zk中存数据, 因此需要指定zk地址. 如果操作生产者则无需指定zk地址;
kfk0.9前消费者将消息偏移量存入zk, kfk0.9开始消费者将offset存入broker. -
创建topic后, 存储生产者发送过来的数据的文件
-
我在
/opt/kafka/kafka/config/
中创建了三个kfk配置文件部署了伪集群:
server-1.properties
和server-2.properties
和server-3.properties
. -
在
server-*.properties
中配置了存放kfk日志和数据的目录(在kfk中数据也叫日志):log.dirs=/tmp/kafka-logs-1
和log.dirs=/tmp/kafka-logs-2
和log.dirs=/tmp/kafka-logs-3
. -
/tmp/kafka-logs-1
目录中有topic01-0
和topic01-1
;
/tmp/kafka-logs-2
目录中有topic01-1
;
/tmp/kafka-logs-3
目录中有topic01-0
; -
可见我们创建了一个topic叫
topic01
, 这个topic有2个分区, 每个分区有两个副本;
两个分区就是topic01-0
和topic01-1
.topic01-0
和topic01-1
各有两个副本, 一个是leader
一个是follower
;
0和1是分区号;
-
-
kfk的日志(非数据)文件
- kfk日志文件目录是
/opt/kafka/kafka/logs/server.log
; 如果使用jps
看不到kfk进程, 查看该文件排查问题; - 找不到日志文件就使用命令
find / -name server.log
获取日志文件路径;
- kfk日志文件目录是
-
删除名字为first的topic
/bin/kafka-topics.sh --delete --zookeeper localhost:2181 --topic first
执行上述命令可能提示下面两行:
Topic first is marked for deletion.
Note: This will have no impact if delete.topic.enable is not set to true.
意思是
名字为first的topic已被标记删除
;只有当
delete.topic.enable
设置为true
时topic才会被物理删除;经过测试, 实际上无论
delete.topic.enable
设置为t还是f, 都会有该提示; -
查看一个topic详情
/bin/kafka-topics.sh --describe --topic topic01 --zookeeper localhost:2181
因为有两个副本, 所以Replicas后面跟了两个数字.
-
注意
kfk副本数不能超过集群节点数(broker)
, 否则创建失败报错;
一个分区的多个副本不能同时存在同一个broker中, 否则不能高可用;
kfk的分区数(partition)可以超过集群节点数(broker)
, 这样做时某个broker中会有多个分区;
2.2.2 命令行测试生产者消费者
-
启动控制台的生产者
/bin/kafka-console-producer.sh --topic topic01 --broker-list localhost:9091
启动生产者时, 需要往指定的topic中发送数据, 因此指定了topic; 需要指定broker; 生产者不和zk交互, 无需指定zk;
-
启动控制台的消费者 – kfk0.9前
/bin/kafka-console-consumer.sh --topic topic01 --zookeeper localhost:2181 # Using the ConsoleConsumer with old consumer is deprecated and will be removed in a future major release. Consider using the new consumer by passing [bootstrap-server] instead of [zookeeper].
启动消费者时, 需要指定从哪个topic消费数据, 因此指定了topic; kfk0.9之后消费者不将offset存到zk, 因此我们指定zk会提示已过时;
-
--from-beginning
参数-
指定该参数可以让新启动的消费者从头开始消费数据, 否则只有当生产者再次生产数据时新启动的消费者才能消费数据; kfk中数据默认存放7天;
/bin/kafka-console-consumer.sh --topic topic01 --zookeeper localhost:2181 --from-beginning
-
-
启动控制台的消费者 – kfk0.9开始
/bin/kafka-console-consumer.sh --topic topic01 --bootstrap-server localhost:9091 --from-beginning
kfk0.9开始消费者将offset存到broker而不是zk, 因此不指定zk而使用
--bootstrap-server
参数指定kfk -
查看
/tmp/kafka-logs-1
和/tmp/kafka-logs-2
和/tmp/kafka-logs-2
- 可见多了很多
__consumer_offsets-数字
, 实际上__consumer_offsets
是kfk内置的topic, 数字代表partition分区编号, 实际该topic默认分区数50, 每个分区只有1个副本; - 我搭建的是3个broker的伪集群. 这50个partition均匀分布在3台broker中;
- 小结: kfk中存数据的地方叫topic, 消费者消费消息的位置(消费消息的偏移量offset)在kfk0.9后存储在kfk的内置topic中, 这个topic叫做__consumer_offsets, 默认50个partition, 每个分区1个副本.
- 可见多了很多
-
注意
- 如果往一个不存在的topic发送消息, 也能发送成功. kfk会自动创建该topic, 该topic默认有1个分区1个副本; 可在server.properties中配置kfk自动创建topic的分区数和副本数;
- 如果指定每个分区2个副本, 指的是leader+follower一共2个, 并不是1个leader和2个follower;
- 同一个partition的leader和follower不能在同一个broker中, 同一个partition的多个follower也不能在同一个broker中;
2.2.3 kafka-topics.sh --describe 显示结果解释
-
查看名字为
mytopic002
的topic的详细信息[root@king tmp]# /opt/kafka/kafka/bin/kafka-topics.sh --describe --topic mytopic002 --zookeeper localhost:2181 Topic:mytopic002 PartitionCount:6 ReplicationFactor:2 Configs: Topic: mytopic002 Partition: 0 Leader: 20 Replicas: 20,10 Isr: 20,10 Topic: mytopic002 Partition: 1 Leader: 10 Replicas: 10,20 Isr: 10,20 Topic: mytopic002 Partition: 2 Leader: 20 Replicas: 20,10 Isr: 20,10 Topic: mytopic002 Partition: 3 Leader: 10 Replicas: 10,20 Isr: 10,20 Topic: mytopic002 Partition: 4 Leader: 20 Replicas: 20,10 Isr: 20,10 Topic: mytopic002 Partition: 5 Leader: 10 Replicas: 10,20 Isr: 10,20
-
第一行显示所有Partition的总结, 以下每一行给出一个Partition中的信息. 因为mytopic002有6个Partition, 所以有6行
-
Leader
: 给出所有Partition中负责读写的broker节点, 每个节点都有可能成为leader -
Replicas
: 给定Partition的副本分布在哪些broker节点上 -
Isr
: 副本都已同步的broker节点集合, 这个集合中所有节点都是存活状态, 并且跟Leader同步 -
第二行信息解释
Topic: mytopic002 Partition: 0 Leader: 20 Replicas: 20,10 Isr: 20,10
- Partition的编号是0, 这个分区属于mytopic002这个topic, Leader副本在
broker.id=20
的这个broker上, 所有的副本分布在broker.id=10
和broker.id=20
这两个broker上. 所有的副本都存活, 并且跟broker.id=20
这个节点同步.
- Partition的编号是0, 这个分区属于mytopic002这个topic, Leader副本在
-
-
ISR的简要解释
- ISR是所有不落后的replica集合, 不落后有两层含义: 距离上次FetchRequest的时间不大于某一个值或落后的消息数不大于某一个值, Leader失败后会从ISR中随机选取一个Follower做Leader, 该过程对用户是透明的
- ISR实际上就是同步速度快 同步条数多的副本的集合, 如果Leader副本挂了, 会从ISR中随机选一个副本作为Leader副本
- ISR是与leader副本保持一定程度同步的副本(ISR包括leader副本)
- ISR是所有不落后的replica集合, 不落后有两层含义: 距离上次FetchRequest的时间不大于某一个值或落后的消息数不大于某一个值, Leader失败后会从ISR中随机选取一个Follower做Leader, 该过程对用户是透明的
2.2.4 kfk常用命令
(详见我的这篇博客:https://blog.csdn.net/weixin_43990804/article/details/111997068)
# 创建topic并指定分区和副本
/opt/kafka/kafka/bin/kafka-topics.sh --create --zookeeper localhost:2181 --topic topic01 --partitions 2 --replication-factor 2
# 查看topic列表
/opt/kafka/kafka/bin/kafka-topics.sh --list --zookeeper localhost:2181
# 查看某个topic的详情
/opt/kafka/kafka/bin/kafka-topics.sh --describe --topic topic01 --zookeeper localhost:2181
# 删除名字为first的topic
/opt/kafka/kafka/bin/kafka-topics.sh --delete --zookeeper localhost:2181 --topic first
# 启动生产者
/opt/kafka/kafka/bin/kafka-console-producer.sh --topic topic01 --broker-list localhost:9091
# 启动消费者 kfk0.9之前
/opt/kafka/kafka/bin/kafka-console-consumer.sh --topic topic01 --zookeeper localhost:2181
# 启动消费者 kfk0.9开始
/opt/kafka/kafka/bin/kafka-console-consumer.sh --topic topic01 --bootstrap-server localhost:9091 --from-beginning
注意: --from-beginning 代表从头消费数据.
# 消费指定topic指定partition中的数据,显示数据的key和value
./bin/kafka-console-consumer.sh --topic topicName --bootstrap-server localhost:9092 --property print.key=true --partition 0 --from-beginning
# 查看消费者组
bin/kafka-consumer-groups.sh --zookeeper localhost:2181 --list 或者
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
# 查看某消费者组详细信息
bin/kafka-consumer-groups.sh --zookeeper localhost:2181 --describe --group consumer_group_name 或者
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group consumer_group_name
注意:
CURRENT-OFFSET表示消费者当前消费到该分区的偏移量。
LOG-END-OFFSET表示当前分区最后的偏移量。
LAG表示消费者“落后”的消费进度。
# 检查topic isr是否完整
./bin/kafka-topics.sh --zookeeper zookeeper.host --describe | less
# 检查缺失isr的topic
./bin/kafka-topics.sh --zookeeper zookeeper.host --describe --under-replicated-partitions
# 检查是否有数据堆积
sh kafka-consumer-offset-checker.sh --zookeeper localhost:2181 --group basp_log --topic topic_name
# 查看当前消费者组的offset
./kafka-consumer-offset-checker.sh -zookeeper localhost:2181 --topic topic_name --group consumer_group_name
# 修改分区数
./kafka-topics.sh --zookeeper localshot:2181 --alter --topic topic_name --partition 18
# 列出消费者组的详细信息
./kafka-run-class.sh kafka.tools.ConsumerOffsetChecker --zookeeper http:2181 --group cousumer_group_name --topic topic_name
# 查看磁盘分布不均衡 (将kfk的数据存放目录设置为data*,方便查询)
du -sh /data*/kafka/log/
# 对topic排序
du /data01/kafka/log/* |sort -k1,1n
2.3 kfk的日志和数据分目录存放
注意data目录需要手动创建一下; logs目录不需要手动创建;