一、Kafka 概述
1.1 定义
Kafka
是一个分布式的基于发布/订阅模式的消息队列(Message Queue
),主要应用于大数据实时处理领域,处于大数据数据传输层,属于消息中间件
。如AB两个系统需要通信,但并不是直连中间通过消息中间件,还可以实现解耦。
1.2 消息队列
1.2.1 传统消息队列的应用场景
(1)同步处理
整个线程串行,并不高效
(2)异步处理
请求数据写入MQ,并返回响应结果,可以做到解耦,等系统上线时去MQ取数据;还可以缓解服务端的压力(削峰)
(3)使用消息队列的好处
1)解耦
允许独立的拓展或者修改两边的处理过程。只要确保它们遵守同样的接口约束。
2)可恢复性
系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
3)缓冲
有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。更多的是解决生产大于消费的问题。
4)灵活性 & 峰值处理能力
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
5)异步通信
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
1.2.2 消息队列的两种方式
(1)点对点模式
消息生产者生产消息发送到queue
中,然后消息消费者从queue
中取出并且消费消息。消息被消费以后,queue
中不再有存储,所以消息消费者不可能消费到已经被消费的消息。queue
支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
总结:一对一
,消费者主动拉去数据,消息收到后消息清除。消息不可复用缺点明显
(2)发布/订阅模式
消息生产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到 topic 的消息会被所有订阅者消费。
总结:一对多
,消费者消费数据后不会清除消息,但毕竟是个消息队列不是类似hdfs
的文件存储系统,数据不会无限期的保存,一定时间后会被删除。
分析:对于消费者消费数据有两种方式:消息队列推数据给消费者、消费者主动拉去数据。对于推
这种形式,是由当前队列来决定,下游的消费者可能处理速度不同,例如消费者1消息处理速度为10M/s
,消费者2消息处理速度为100M/s
,但是消息队列推出消息的速度统一为50M/s
,因此消费者1直接崩溃,消费者2造成资源浪费。Kafka
是基于第二种,消费者主动拉取数据,因此消费者的消费速度可以由消费者自己决定。但是这种模式也有一个缺点,消费者主动拉取数据一定要做长轮询
,如果消息队列长时间没有推送数据也会造成一定的资源浪费。
1.3 Kafka 基础架构
1)Producer
:消息生产者,就是向 kafka broker
发消息的客户端
2)Consumer
:消息消费者,向 kafka broker
取消息的客户端
3)Consumer Group (CG)
:消费者组,由多个 consumer
组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者,提高消费能力但一个消费者组理论上不高于一个消息主题的分区个数。
4)Broker
:一台 kafka
服务器就是一个 broker
。一个集群由多个broker
组成。一个broker
可以容纳多个topic
,kafka
的多节点使用的zk
是同一个集群那么就能组成kafka
集群
5)Topic
:可以理解为一个队列,生产者和消费者面向的都是一个 topic
,作用为分类消息。
6)Partition
:为了实现扩展性,一个非常大的 topic
可以分布到多个broker
(即服务器)上,一个 topic
可以分为多个partition
,每个 partition
是一个有序的队列
7)Replica
:副本,为保证集群中的某个节点发生故障时,该节点上的 partition
数据不丢失,且kafka
仍然能够继续工作,kafka
提供了副本机制,一个 topic 的每个分区都有若干个副本,一个 leader
和若干个follower
8)leader
:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是leader
9)follower
:每个分区多个副本中的“从”,实时从 leader
中同步数据,保持和 leader
数据的同步。leader
发生故障时,某个 follower
会成为新的 follower
10)zk
:帮助kafka
集群存储一些信息;帮助消费者存储消费位置信息。0.9
版本后存储在kafka
本地(不是本地磁盘)
1.3.1 总结
首先群起kafka
集群,brocker
相当于一个节点起的一个kafka
进程,消息都是存储在brocker
中,如果只有一个brocker
存储数据容易乱套,如多个生产者生产数据都要使用kafka
会导致消息数据混乱。这时候就需要topic
,相当于把许多消息进行分类,消费者按需求去对应的topic
取消息数据;同一个主题会在不同的brocker
存在,即进行分区,提高某个节点的负载均衡,将来消息数据生产出来,均分给不同节点的相同topic
,一定意义上提高了集群的并发和负载能力;同时一个分区topic
对应一个leader
和一个follower
,这个follower
相当于备份作用提供集群的高可用(只有在leader
挂了才会起作用,正常情况消费者只从leader
取数据),因此同一个分区topic
的leader
和一个follower
必定是在两个不同的节点上,将来leader
所在的节点挂了可以将follower
提升为leader
继续工作。
对于消费者有个消费者组概念,可以把多个消费者用一个组名,同时消费者组有一个特点:一个分区只能被一个组的一个消费者消费,大大提高消费能力,但一个消费者组的消费者个数不能大于一个主题的分区数否则会造成资源的浪费,因此理想的并发度是消费者组的消费者个数等于某个主题的分区数。
对于zk
会帮助kafka
集群存储一些信息,同时在接下来搭建kafka
集群的时候会发现没有所谓hdfs
的namenode
,kafka
集群则是靠使用一个集群的zk
来组成自己的集群,即只要kafka
各个节点使用的zk
是同一个集群,那么我就是一个kafka
集群。
对于消费者最重要的是要实现类似断点续传功能,即一个消费者挂掉重启后还可以接着原来的位置继续消费。在0.9
版本之前将偏移量offset
存储在zk
中,但是0.9
之后将其存储在kafka
中的系统topic
中(本地磁盘,默认存储7天)。why?考虑到消费者既要连接kafka
消费数据同时要实时的向zk
中存储数据,效率低下。
二、Kafka 快速入门
2.1 集群搭建
2.1.1 上传压缩包并解压
tar -zxvf kafka_2.13-2.5.0.tgz
2.13
代表scala
版本,2.5.0
为kafka
版本
2.1.2 修改配置文件
cd kafka_2.13-2.5.0/config/
# The id of the broker. This must be set to a unique integer for each broker
broker.id=0
# A comma separated list of directories under which to store log files
log.dirs=/usr/local/soft/kafka_2.13-2.5.0/logs
# Zookeeper connection string (see zookeeper docs for details).
# This is a comma separated host:port pairs, each corresponding to a zk
# server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002".
# You can also append an optional chroot string to the urls to specify the
# root directory for all kafka znodes.
zookeeper.connect=master:2181,slave01:2181,slave02:2181
2.1.3 配置环境变量并分发
分发脚本
#!/bin/bash
#1 获取输入参数个数,如果没有参数,直接退出
pcount=$#
if [ $pcount == 0 ]
then
echo no args
exit
fi
#2 获取文件名称
p1=$1
fname=`basename $p1`
echo fname=$fname
#3 获取上级目录到绝对路径
pdir=`cd -P $(dirname $p1); pwd`
echo pdir=$pdir
#4 获取当前用户名称
user=`whoami`
rsync -rvl $pdir/$fname $user@slave01:$pdir
rsync -rvl $pdir/$fname $user@slave02:$pdir
2.1.4 编写群起脚本
kafka
没有实现类似hdfs
中workers
的群起集群功能,需要我们一个个节点启动(类似zk
),因此需要自己编写群起脚本
#!/bin/bash
pcount=$#
if [ $pcount == 0 ]
then
echo "no args"
exit
fi
case $1 in
"start"){
echo ""
echo "—————————————————————————————————————————"
for i in master slave01 slave02
do
echo ""
echo " $i start kafka successfully "
echo ""
ssh $i "/usr/local/soft/kafka_2.13-2.5.0/bin/kafka-server-start.sh -daemon /usr/local/soft/kafka_2.13-2.5.0/config/server.properties"
done
echo "—————————————————————————————————————————"
echo ""
};;
"stop"){
echo ""
echo "—————————————————————————————————————————"
for i in master slave01 slave02
do
echo ""
echo " $i stop kafka successfully "
echo ""
ssh $i "/usr/local/soft/kafka_2.13-2.5.0/bin/kafka-server-stop.sh /usr/local/soft/kafka_2.13-2.5.0/config/server.properties"
done
echo "—————————————————————————————————————————"
echo ""
};;
esac
注意:kafka关闭会有延迟,也就是说关闭后立刻jps可能还能看到kafka进程,稍等一下即可
2.2 Kafka 命令行操作
(1)创建一个主题
kafka-topics.sh --zookeeper master:2181 --create --topic first --partitions 2 --replication-factor 2
因为kafka
集群需要将元数据写入zk
中(0.9
版本后消费者的数据不写入zk
),因此需要连接zk
集群同时指定分区数和副本数(副本数有坑,注意和hdfs
的副本数区分,一会有shell演示),根据配置文件进入logs
文件夹(后面会将日志和消息数据分开),并查看所有节点的logs
文件夹。
first-0 first-1
表示分区数,即kafka
以主题名和分区数对topic
进行命名且以文件夹的形式,纵观三台节点可以发现副本数也是两个。
(2)查看所有的topic
kafka-topics.sh --zookeeper master:2181 --list
(3)查看topic
的详细信息
kafka-topics.sh --zookeeper master --describe --topic first
Topic: first PartitionCount: 2 ReplicationFactor: 2 Configs:
Topic: first Partition: 0 Leader: 2 Replicas: 2,0 Isr: 2,0
Topic: first Partition: 1 Leader: 0 Replicas: 0,1 Isr: 0,1
可以看出该topic
有2个分区(first-0
,first-1
)和2个副本,其中first-0
在broker.id
为0和2上,且leader
是broker.id
为2的分区;first-1
在broker.id
为0和1上,且leader
是broker.id
为0的分区,图示如下:
(4)删除topic
kafka-topics.sh --zookeeper master:2181 --delete --topic first
Topic first is marked for deletion.
Note: This will have no impact if delete.topic.enable is not set to true.
执行删除的topic
不会立刻删除而是被标记一下,随后才会被删除。
(5)浅谈副本数
在创建topic
时需要指定副本数即replication-factor
,很容易联想到hdfs
的副本数,在hdfs
中副本数可以配置任意值即使超过集群数,它的策略是当增加集群数时会自己拷贝副本数知道达到配置的副本数;在kafka
并不是这样,当创建的副本数大于可用的broker
时会报错,即原先的kafka
集群有三个broker
此时创建三个副本的topic
时不会报错,这时候突然有一个节点宕机原先的topic
不受影响但是随后不能再创建三个副本的topic
,一句话总结就是:hdfs
的副本数是最大副本数,kafka
的就是现在的副本数。
kafka-topics.sh -zookeeper master:2181 --partitions 4 --replication-factor 4
Error while executing topic command : Replication factor: 4 larger than available brokers: 3.
[2020-07-30 17:08:06,756] ERROR org.apache.kafka.common.errors.InvalidReplicationFactorException: Replication factor: 4 larger than available brokers: 3.
(kafka.admin.TopicCommand$)
但是分区不受影响。
kafka-topics.sh -zookeeper master:2181 --create --topic first--partitions 4 --replication-factor 4
查看topic
的详细信息
Topic: first PartitionCount: 4 ReplicationFactor: 3 Configs:
Topic: first Partition: 0 Leader: 2 Replicas: 2,0,1 Isr: 2,0,1
Topic: first Partition: 1 Leader: 0 Replicas: 0,1,2 Isr: 0,1,2
Topic: first Partition: 2 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
Topic: first Partition: 3 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
(6)日志数据分离
为了使日志和消息数据分开存储,我们需要将其分离,步骤如下
- 关闭
kafka
集群,删除logs
文件夹 - 进入
zk
删除kafka
相关[除了自己的东西其他全部删除] - 修改配置文件
log.dirs
[这个其实是配置数据的地方] - 启动
kafka
集群
已经实现了日志数据分离的效果,可以通过创建一个topic
来验证一下
(7)控制台测试生产者、消费者
常用命令都放在kafka/bin
中,见名知意可知需要的命令为
kafka-console-consumer.sh
kafka-console-producer.sh
- 生产者测试
生产者直接连接kafka
向指定主题生产数据,因此代码如下:
kafka-console-producer.sh --bootstrap-server master:9092 --topic first
会进入kafka
客户端,等待生产数据
- 消费者测试
上面说过0.9
版本消费者的消费记录存储在kafka
系统维护的主题(一会就能看到),因此消费者的代码也是一样
kafka-console-consumer.sh --bootstrap-server master:9092 --topic first
窗口会进入阻塞状态等待消费数据,此时可以在生产者那随便生产一点数据观察现象
kafka
是基于订阅/发布模式,且消费者消费数据是拉取模式,因此kafka
消息队列即使客户端不在线,当上线时会消费以前的数据(默认消费7天以内),注意:上面的测试代码会读取消费记录,即当重新上线不会重复消费,--from-beginning
参数会从头消费。当新建一个消费者会消费离线时发布的数据,同时查看data
文件可以发现,分区以一种轮换的方式进行。