什么是 Kafka
Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因素。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。 对于像Hadoop一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka的目的是通过Hadoop的并行加载机制来统一线上和离线的消息处理,也是为了通过集群来提供实时的消息。
消息收发模式
- P2P 模式:发送到队列中的消息被一个而且仅仅一个消费者所接收,即使有多个消费者在同一个队列中监听同一消息。
-
Pub/Sub 模式 :Pub/Sub 模式通常用于一对多或多对多的消息群发场景,即拥有一个或多个消息发送者和多个消息接收者的场景。
-
P2P 模式和 Pub/Sub 模式的区别
发送消息时,Pub/Sub 模式下,发送者需要按照和接收者约定好的 Topic 发送消息;而 P2P 模式下,发送者无需事先约定传输消息的 Topic,发送者可以直接按照规范发送消息到目标的接收者。
接收消息时,Pub/Sub 模式下,接收者需要按照和发送者约定好的 Topic 提前订阅才能收到消息;而 P2P 模式下接收者无需事先订阅即可接收消息,从而简化接收者的程序逻辑,节省订阅成本。
Kafka架构
kafka 是一个高性能的分布式发布/订阅消息系统。一个典型的Kafka集群中包含若干Producer,若干broker(一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。
-
Producer端直接连接broker.list列表,从列表中返回TopicMetadataResponse,该Metadata包含Topic下每个partition leader,Producer和partition leader建立socket连接并发送消息.
-
Broker端使用zookeeper用来注册broker信息,以及监控partition leader存活性.
-
Consumer端使用zookeeper来注册consumer信息,其中包括consumer消费的partition列表等,同时也用来发现broker列表,并和partition leader建立socket连接,并获取消息。
-
Producer
生产者,负责发布消息到Kafka的服务器。由于kakfa是一个Pub/Sub的消息系统,所以Producer也称为发布者。 -
Broker
安装了Kafka的服务器,被称为broker,也叫kafka server。 -
Consumer
消息消费者,向Kafka broker读取消息的客户端。由于kakfa是一个Pub/Sub的消息系统,所以Consumer也称为订阅者 -
Topic
每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处) -
Partition
Partition分区是物理上的概念,每个Topic包含一个或多个Partition -
Consumer Group
每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group) -
Push
推送(PUSH)技术是一种建立在客户服务器上的机制,就是由服务器主动将信息发往客户端的技术。PUSH技术的优势在于信息的主动性和及时性。 -
Pull
拉(PULL)技术则是由客户机主动请求信息。
搭建Kafka伪集群
我在本地机器上搭建一个kafka集群
- 下载
Kafka官方下载地址:http://kafka.apache.org/
这次我下载的是kafka_2.13-2.6.0.tgz - kafka 目录
创建3个服务节点node1,node2,node3 用于存放kafka
mkdir /kafka/node1
mkdir /kafka/node2
mkdir /kafka/node3 - kafka的log.dirs 目录
创建3个日志目录分别存放3个节点的消息数据
mkdir /kafka/log/node1
mkdir /kafka/log/node2
mkdir /kafka/log/node3 - Kafka的server.properties配置
#进入应用目录
cd /kafka/node1/config
#修改配置文件
vi config/server.properties
节点配置信息
节点 | broker.id | log.dirs | listeners |
---|---|---|---|
node1 | broker.id=0 | log.dirs=/kafka/log/node1 | listeners=PLAINTEXT://:9092 |
node2 | broker.id=1 | log.dirs=/kafka/log/node2 | listeners=PLAINTEXT://:9093 |
node3 | broker.id=2 | log.dirs=/kafka/log/node3 | listeners=PLAINTEXT://:9094 |
启动Broker集群
- 启动zookeeper
bin/zookeeper-server-start.sh config/zookeeper.properties
- 启动kafka
分别在node1 ,node2 ,node3 中执行以下脚本
/bin/kafka-server-start.sh config/server.properties &
- 创建topic
ZBMAC-2f32839f6:node1 yangyanping$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 3 --topic test1
Created topic test1.
术语 | 描述 |
---|---|
partions | 主题分区数。kafka通过分区策略,将不同的分区分配在一个集群中的broker上,一般会分散在不同的broker上,当只有一个broker时,所有的分区就只分配到该Broker上。消息会通过负载均衡发布到不同的分区上,消费者会监测偏移量来获取哪个分区有新数据,从而从该分区上拉取消息数据。分区数越多,在一定程度上会提升消息处理的吞吐量,因为kafka是基于文件进行读写,因此也需要打开更多的文件句柄,也会增加一定的性能开销。如果分区过多,那么日志分段也会很多,写的时候由于是批量写,其实就会变成随机写了,随机 I/O 这个时候对性能影响很大。所以一般来说 Kafka 不能有太多的 Partition。 |
replication-factor | 用来设置主题的副本数。每个主题可以有多个副本,副本位于集群中不同的broker上,也就是说副本的数量不能超过broker的数量,否则创建主题时会失败。 |
查看topic信息
- 我们可以通过命令列出指定Broker上的topic
ZBMAC-2f32839f6:node1 yangyanping$ bin/kafka-topics.sh --list --bootstrap-server 127.0.0.1:9092
__consumer_offsets
test1
- 查看主题分区信息
bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic test1
Topic: test1 PartitionCount: 3 ReplicationFactor: 3 Configs:
Topic: test1 Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
Topic: test1 Partition: 1 Leader: 2 Replicas: 2,0,1 Isr: 2,0,1
Topic: test1 Partition: 2 Leader: 0 Replicas: 0,1,2 Isr: 0,1,2
通过上边的描述信息,可以得到如下图分配方式。主题test1分区信息如图,绿色为Leader 分区,红色为 副本分区
查看元数据信息
- 使用./zkCli.sh 连接zookeeper
****-2f32839f6:zookeeper yangyanping$ ./bin/zkCli.sh
/usr/bin/java
Connecting to localhost:2181
- 查看kakaf 目录结构
[zk: localhost:2181(CONNECTED) 0] ls /
[admin, brokers, cluster, config, consumers, controller, controller_epoch, isr_change_notification, latest_producer_id_block, log_dir_event_notification, zookeeper]
[zk: localhost:2181(CONNECTED) 3] ls /brokers
[ids, seqid, topics]
[zk: localhost:2181(CONNECTED) 5] ls /brokers/ids
[0, 1, 2]
[zk: localhost:2181(CONNECTED) 6] get /brokers/ids/0
{"listener_security_protocol_map":{"PLAINTEXT":"PLAINTEXT"},"endpoints":["PLAINTEXT://10.0.72.201:9092"],"jmx_port":-1,"port":9092,"host":"10.0.72.201","version":4,"timestamp":"1606962873700"}
- 查看config目录信息
[zk: localhost:2181(CONNECTED) 11] ls /config
[brokers, changes, clients, topics, users]
[zk: localhost:2181(CONNECTED) 13] ls /config/topics
[test1]
- 查看controller信息
[zk: localhost:2181(CONNECTED) 18] ls /controller
[]
[zk: localhost:2181(CONNECTED) 19] get /controller
{"version":1,"brokerid":0,"timestamp":"1606962873843"}
- 查看分区信息
[zk: localhost:2181(CONNECTED) 21] ls /brokers
[ids, seqid, topics]
[zk: localhost:2181(CONNECTED) 22] ls /brokers/ids
[0, 1, 2]
[zk: localhost:2181(CONNECTED) 23] get /brokers/ids/0
{"listener_security_protocol_map":{"PLAINTEXT":"PLAINTEXT"},"endpoints":["PLAINTEXT://10.0.72.201:9092"],"jmx_port":-1,"port":9092,"host":"10.0.72.201","version":4,"timestamp":"1606962873700"}
[zk: localhost:2181(CONNECTED) 24] ls /brolers/ids/0
Node does not exist: /brolers/ids/0
[zk: localhost:2181(CONNECTED) 25] ls /brokers/ids/0
[]
[zk: localhost:2181(CONNECTED) 26] ls /brokers/topics
[test1]
[zk: localhost:2181(CONNECTED) 27] ls /brokers/topics/test1
[partitions]
[zk: localhost:2181(CONNECTED) 28] ls /brokers/topics/test1/partitions
[0, 1, 2]
[zk: localhost:2181(CONNECTED) 29] ls /brokers/topics/test1/partitions/0
[state]
[zk: localhost:2181(CONNECTED) 30] ls /brokers/topics/test1/partitions/0/state
[]
[zk: localhost:2181(CONNECTED) 31] get /brokers/topics/test1/partitions/0/state
{"controller_epoch":3,"leader":1,"version":1,"leader_epoch":0,"isr":[1,2,0]}
发送和消费消息
- 消费消息
通过 kafka-console-consumer.sh 脚本来订阅主题 test1
ZBMAC-2f32839f6:node1 yangyanping$ bin/kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --topic test1 --from-beginning
hello kafka
hello world
- 这里我们向Broker(id=0)的Topic=test1发送消息
通过使用 kafka-console-producer.sh 脚本 发送消息
ZBMAC-2f32839f6:node1 yangyanping$ bin/kafka-console-producer.sh --broker-list 127.0.0.1:9092 --topic test1
>hello kafka
>hello world
我们启动Consumer指定Consumer Group Id就可以作为一个消费组协同工,1个消息同时只会被一个Consumer消费到
ZBMAC-2f32839f6:node1 yangyanping$ bin/kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --topic test1 --from-beginning --group testgroup1
Hello kafka
ZBMAC-2f32839f6:node3 yangyanping$ bin/kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --topic test1 --from-beginning --group testgroup1
Hello world
Kafka在Zookeeper上的目录结构
-
kafka使用zookeeper来实现动态的集群扩展,不需要更改客户端(producer和consumer)的配置。broker会在zookeeper注册并保持相关的元数据(topic,partition信息等)更新。
-
Broker端使用zookeeper来注册broker信息,以及监测partition leader存活性.
-
Zookeer和Producer没有建立关系,只和Brokers、Consumers建立关系以实现负载均衡,即同一个ConsumerGroup中的Consumers可以实现负载均衡(因为Producer是瞬态的,可以发送后关闭,无需直接等待
-
节点/brokers/ids 内容
即/brokers/ids/[brokerId])存储“活着”的broker信息。主要使用zookeeper的服务发现功能。
{
"listener_security_protocol_map":{
"PLAINTEXT":"PLAINTEXT"
},
"endpoints":[
"PLAINTEXT://127.0.0.1:9092"
],
"jmx_port":-1,
"port":9092,
"host":"127.0.0.1",
"version":4,
"timestamp":"1607584166755"
}
- 节点/brokers/ids/1 内容
{
"listener_security_protocol_map":{
"PLAINTEXT":"PLAINTEXT"
},
"endpoints":[
"PLAINTEXT://127.0.0.1:9093"
],
"jmx_port":-1,
"port":9093,
"host":"127.0.0.1",
"version":4,
"timestamp":"1607584196139"
}
- 节点/brokers/ids/2 内容
{
"listener_security_protocol_map":{
"PLAINTEXT":"PLAINTEXT"
},
"endpoints":[
"PLAINTEXT://127.0.0.1:9094"
],
"jmx_port":-1,
"port":9094,
"host":"127.0.0.1",
"version":4,
"timestamp":"1607584219795"
}
- 节点/brokers/topic/test1(主题)/partitions/0/sate 内容
topic注册信息(/brokers/topics/[topic]),存储该topic的所有partition的所有replica所在的broker id,第一个replica即为preferred replica,对一个给定的partition,它在同一个broker上最多只有一个replica,因此broker id可作为replica id。
{
"controller_epoch":1,
"leader":1,
"version":1,
"leader_epoch":0,
"isr":[
1,
0,
2
]
}
- 节点/brokers/topic/test1(主题)/partitions/1/sate 内容
{
"controller_epoch":1,
"leader":2,
"version":1,
"leader_epoch":0,
"isr":[
2,
1,
0
]
}
- 节点/brokers/topic/test1(主题)/partitions/2/sate 内容
{
"controller_epoch":1,
"leader":0,
"version":1,
"leader_epoch":0,
"isr":[
0,
2,
1
]
}
- 节点/controller 内容(conrtoller leader信息)
/controller -> int (broker id of the controller)存储当前controller的信息。 保存kafka集群的leader 信息,主要使用zookeeper 的leader 选举功能。
{
"version":1,
"brokerid":0,
"timestamp":"1607653391978"
}
- 节点controller_epoch
/controller_epoch -> int (epoch)直接以整数形式存储controller epoch,而非像其它znode一样以JSON字符串形式存储。 - admin节点
该目录下znode只有在有相关操作时才会存在,操作结束时会将其删除
/admin/reassign_partitions用于将一些Partition分配到不同的broker集合上。对于每个待重新分配的Partition,Kafka会在该znode上存储其所有的Replica和相应的Broker id。该znode由管理进程创建并且一旦重新分配成功它将会被自动移除