推送速率难以适应消费速率
,推模式的目标就是以最快的速度推送消息,不同的消费者的消费速率还不一样
,身为 Broker 很难平衡每个消费者适用于消息量不大、消费能力强要求实时性高的情况下
优点
消费者可以根据自身的情况来发起拉取消息的请求
。假设当前消费者觉得自己消费不过来了,它可以根据一定的策略停止拉取,或者间隔拉取都行。Broker 就相对轻松
了,它只管存生产者发来的消息,至于消费的时候自然由消费者主动发起,来一个请求就给它消息呗,从哪开始拿消息,拿多少消费者都告诉它,它就是一个没有感情的工具人,消费者要是没来取也不关它的事。缺点
消息延迟
,毕竟是消费者去拉取消息,但是消费者怎么知道消息到了呢?所以它只能不断地拉取,但是又不能很频繁地请求,太频繁了就变成消费者在攻击 Broker 了。因此需要降低请求的频率,比如隔个 2 秒请求一次,你看着消息就很有可能延迟 2 秒了。消息忙请求
,忙请求就是比如消息隔了几个小时才有,那么在几个小时之内消费者的请求都是无效的,在做无用功。服务端压力变大
,长时间消费速度跟不上生产速度,就会使得Broker存储的消息越来越多,极端情况下可能导致服务器内存爆仓适用场景
更合适的进行消息的批量发送
,基于推模式可以来一个消息就推送,也可以缓存一些消息之后再推送,但是推送的时候其实不知道消费者到底能不能一次性处理这么多消息。而拉模式就更加合理,它可以参考消费者请求的信息来决定缓存多少消息之后批量发送。一个分区的数据,只能由消费者组内的一个消费者消费,避免重复消费问题
如果没有对消费者分组,那么一个分区的数据可以被多个消费者消费
消费者只能从主partition进行消费,当主partition挂掉后,从partition可成为主partition
官网地址:https://kafka.apache.org/downloads
wget https://archive.apache.org/dist/kafka/3.0.0/kafka_2.12-3.0.0.tgz -P /opt/software
解压
tar -zxvf /opt/software/kafka_2.12-3.0.0.tgz -C /opt/moudle
重命名
mv /opt/moudle/kafka_2.12-3.0.0 /opt/moudle/kafka
server.properties
# 集群中每个broker的身份标识,整个集群中不能存在重复的id
broker.id=0
# 数据存储目录,建议不要使用/tmp临时路径
# log.dirs=/tmp/kafka-logs
log.dirs=/opt/moudle/kafka/datas
# 连接的zookeeper集群
# /kafka为了方便管理数据
# zookeeper.connect=localhost:2181
zookeeper.connect=first-node:2181,second-node:2181,third-node:2181/kafka
分发kafka软件包
xsync /opt/moudle/kafka
修改集群其他节配置文件的broker.id
ssh second-node "sed -i.bak s/broker.id=0/broker.id=1/ /opt/moudle/kafka/config/server.properties"
ssh third-node "sed -i.bak s/broker.id=0/broker.id=2/ /opt/moudle/kafka/config/server.properties"
配置kafka环境变量:sudo vim /etc/profile.d/my_env.sh
# KAFKA_HOME
export KAFKA_HOME=/opt/moudle/kafka
export PATH=$PATH:$KAFKA_HOME/bin
重新加载环境变量
source /etc/profile
环境变量配置文件分发
root
用户未配置ssh免密登录,此处需要输入密码sudo /home/fatpuffer/bin/xsync /etc/profile.d/my_env.sh
登录集群环境其他节点重新加载环境变量
source /etc/profile
zk.sh start
kafka-server-start.sh -daemon /opt/moudle/kafka/config/server.properties
kafka-server-stop.sh
1.用户bin目录下创建kfk.sh文件:vim /home/fatpuffer/bin/kfk.sh
#! /bin/bash
case $1 in
"start"){
for node in first-node second-node third-node
do
echo ----------------------kafka $node start--------------------
ssh $node "kafka-server-start.sh -daemon /opt/moudle/kafka/config/server.properties"
done
};;
"stop"){
for node in first-node second-node third-node
do
echo ----------------------kafka $node stop--------------------
ssh $node "kafka-server-stop.sh"
done
};;
esac
2.修改脚本权限
comod +x kfk.sh
3.脚本使用
kfk.sh start
kfk.sh stop
查看节点/kafka
信息
[zk: localhost:2181(CONNECTED) 1] ls /kafka
[admin, brokers, cluster, config, consumers, controller_epoch, feature, isr_change_notification, latest_producer_id_block, log_dir_event_notification]
查看节点/kafka/brokers
[zk: localhost:2181(CONNECTED) 16] ls /kafka/brokers
[ids, seqid, topics]
查看节点/kafka/brokers/ids
[zk: localhost:2181(CONNECTED) 11] ls /kafka/brokers/ids
[0, 1, 2]
查看节点/kafka/brokers/topics
[zk: localhost:2181(CONNECTED) 17] ls /kafka/brokers/topics
[]
获取/kafka/brokers/ids/0
的数据
[zk: localhost:2181(CONNECTED) 13] get /kafka/brokers/ids/0
{"listener_security_protocol_map":{"PLAINTEXT":"PLAINTEXT"},"endpoints":["PLAINTEXT://first-node:9092"],"jmx_port":-1,"features":{},"host":"first-node","timestamp":"1670480025960","port":9092,"version":5}
[zk: localhost:2181(CONNECTED) 14] get /kafka/brokers/ids/1
{"listener_security_protocol_map":{"PLAINTEXT":"PLAINTEXT"},"endpoints":["PLAINTEXT://second-node:9092"],"jmx_port":-1,"features":{},"host":"second-node","timestamp":"1670480032519","port":9092,"version":5}
[zk: localhost:2181(CONNECTED) 15] get /kafka/brokers/ids/2
{"listener_security_protocol_map":{"PLAINTEXT":"PLAINTEXT"},"endpoints":["PLAINTEXT://third-node:9092"],"jmx_port":-1,"features":{},"host":"third-node","timestamp":"1670480038549","port":9092,"version":5}
命令参数
参数 | 描述 |
---|---|
–bootstrap-server |
连接的 Kafka Broker主机名称和端口 |
–topic |
操做的Topic名称 |
–create | 创建主题 |
–delete | 删除主题 |
–alter | 修改主题 |
–list | 查看所有主题 |
–describe | 查看主题详细描述 |
–partitions |
设置分区数 |
–replication-factor |
设置分区副本 |
–config |
更新系统默认的配置 |
–producer-property | 将自定义属性传递给生成器的机制,形如:key=value |
–producer.config | 生产者配置属性文件 [–producer-property] 优先于此配置 |
–property | 自定义消息读取器:parse.key=true|false key.separator= ignore.error=true |
–request-required-acks | 生产者请求的确认方式:0、1(默认值)、all |
–sync | 同步发送消息 |
查看当前服务器中的所有topic
kafka-topics.sh --bootstrap-server first-node:9092 --list
# 连接多个节点
# kafka-topics.sh --bootstrap-server first-node:9092,second-node:9092,third-node:9092 --list
创建一个名为demo1
的主题,设置分区数为1,分区副本为3
kafka-topics.sh --bootstrap-server first-node:9092 --topic demo1 --create --partitions 1 --replication-factor 3
[zk: localhost:2181(CONNECTED) 22] ls /kafka/brokers/topics
[demo1]
[zk: localhost:2181(CONNECTED) 23] ls /kafka/brokers/topics/demo1
[partitions]
[zk: localhost:2181(CONNECTED) 24] ls /kafka/brokers/topics/demo1/partitions
[0]
[zk: localhost:2181(CONNECTED) 25] ls /kafka/brokers/topics/demo1/partitions/0
[state]
[zk: localhost:2181(CONNECTED) 26] ls /kafka/brokers/topics/demo1/partitions/0/state
[]
[zk: localhost:2181(CONNECTED) 27] get /kafka/brokers/topics/demo1
{"removing_replicas":{},"partitions":{"0":[2,1,0]},"topic_id":"D6Lse1d5TduQCu0mh-idwQ","adding_replicas":{},"version":3}
[zk: localhost:2181(CONNECTED) 28] get /kafka/brokers/topics/demo1/partitions/0/state
{"controller_epoch":5,"leader":2,"version":1,"leader_epoch":0,"isr":[2,1,0]}
查看demo1
主题详情
kafka-topics.sh --bootstrap-server first-node:9092 --describe demo1
# 分区数 # 分区副本数 # 分区大小
Topic: demo1 TopicId: D6Lse1d5TduQCu0mh-idwQ PartitionCount: 1 ReplicationFactor: 3 Configs: segment.bytes=1073741824
# 分区开始位置 # 副本中的leader对应的broker # 分区副本对应的broker(这三个副本分别存储在broker.id=2、broker.id=1、broker.id=0)
Topic: demo1 Partition: 0 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
修改主题demo1
分区数为3
kafka-topics.sh --bootstrap-server first-node:9092 --topic demo1 --alter --partitions 3
[zk: localhost:2181(CONNECTED) 34] ls /kafka/brokers/topics
[demo1]
[zk: localhost:2181(CONNECTED) 35] ls /kafka/brokers/topics/demo1
[partitions]
[zk: localhost:2181(CONNECTED) 36] ls /kafka/brokers/topics/demo1/partitions
[0, 1, 2]
[zk: localhost:2181(CONNECTED) 37] ls /kafka/brokers/topics/demo1/partitions/1
[state]
[zk: localhost:2181(CONNECTED) 38] ls /kafka/brokers/topics/demo1/partitions/1/state
[]
[zk: localhost:2181(CONNECTED) 39] get /kafka/brokers/topics/demo1
{"removing_replicas":{},"partitions":{"2":[1,2,0],"1":[0,1,2],"0":[2,1,0]},"topic_id":"D6Lse1d5TduQCu0mh-idwQ","adding_replicas":{},"version":3}
[zk: localhost:2181(CONNECTED) 40] get /kafka/brokers/topics/demo1/partitions/0/state
# 0号分区leader为broker2
{"controller_epoch":5,"leader":2,"version":1,"leader_epoch":0,"isr":[2,1,0]}
[zk: localhost:2181(CONNECTED) 41] get /kafka/brokers/topics/demo1/partitions/1/state
# 1号分区leader为broker0
{"controller_epoch":5,"leader":0,"version":1,"leader_epoch":0,"isr":[0,1,2]}
[zk: localhost:2181(CONNECTED) 55] get /kafka/brokers/topics/demo1/partitions/2/state
# 2号分区leader为broker1
{"controller_epoch":5,"leader":1,"version":1,"leader_epoch":0,"isr":[1,2,0]}
[fatpuffer@first-node ~]$ kafka-topics.sh --bootstrap-server first-node:9092 --describe demo1
Topic: demo1 TopicId: D6Lse1d5TduQCu0mh-idwQ PartitionCount: 3 ReplicationFactor: 3 Configs: segment.bytes=1073741824
Topic: demo1 Partition: 0 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: demo1 Partition: 1 Leader: 0 Replicas: 0,1,2 Isr: 0,1,2
Topic: demo1 Partition: 2 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
生产者向broker中的demo1
主题发送一条消息
kafka-console-producer.sh --bootstrap-server first-node:9092 --topic demo1
消费者连接broker,并且监听deno1
主题,broker监控到demo1
主题中有消息产生,则将该消息推送给所有demo1
主题的监听者
kafka-console-consumer.sh --bootstrap-server first-node:9092 --topic demo1
# --from-beginning可以获取历史数据
kafka-console-consumer.sh --bootstrap-server first-node:9092 --topic demo1 --from-beginning
发送带key
的消息
kafka-console-producer.sh --bootstrap-server first-node:9092 --topic demo1 --producer-property parse.key=true
tab
键分割在消息发送过程中,涉及到了两个线程,main线程
和sender
线程。在main线程中创建了一个双端队列:RecordAccumulator
。main线程将消息发送给:RecordAccumulator,sender
线程不断从:RecordAccumulator中拉取消息发送到:Kafka Broker
创建配置对象
向配置对象写入kafka集群连接信息
向配置对象写入序列化信息
根据配置对象,创建生产者对象
使用生产者对象发送数据
关闭生产者对象
kafka-python:https://kafka-python.readthedocs.io/en/master/apidoc/KafkaConsumer.html
便于合理使用存储资源
,每个Partion在一个Broker上存储,可以把海量的数据按照分区切割成一块一块数据存储在多台Broker上。合理控制分区的任务,可以实现负载均衡
的效果。提高并行度
,生产者可以以分区为单位发送数据;消费者可以以分区为单位进行消费数据指定了分区
,则所有消息发送到该分区未指定分区
,但指定了key
未指定分区
,也未指定key
Kafka
采用Sticky Partition(黏性分区器)
,会随机选择一个分区,并尽可能一直使用该分区,等待该分区的batch.size
已满或已完成,Kafka
再随机一个分区进行使用(和上一次的分区不同)
linger.ms
设置的时间到了,Kafka
再随机选择一个分区进行使用(如果是0,还会继续随机)hashcode
------------------------->Python:ord("a")
linger.ms
时常,越接近batch.size:16kb
,则效率越高
batch.size
:传输量,16或32linger.ms
:延迟,5-10mscompression.type
:压缩snappyack=0
,可靠性差,效率高acks=0
,生产者发送过来数据就不管了,可靠性差,效率高acks=1
,生产者发送过来数据,Leader(落盘)应答,可靠性中等,效率中等acks=-1
,生产者发送过来数据,Leader和ISR队列里面的所有Follower应答,可靠性高,效率低acks=0
很少使用;acks=1
一般使用传输普通日志,允许丢个别;acks=-1
一般用于和钱相关的数据,对可靠性要求比较高的场景。acks=-1(all)
:生产者发送过来的数据,Leader和ISR队列里面的所有节点收齐后应答
-1
+ 分区副本大于等于2
+ ISR里应答得最小副本数大于等于2
0
At Least Once
:可以保证数据不丢失,但是不能保证数据不重复
At Most Once
:可以保证数据不重复,但是不能保证数据不丢失
既不能重复,也不能丢失
幂等性和事务
Producer
不论向Broker
发送多少次重复数据,Broker
端都只会持久化一条
,保证了不重复。ack = -1 + 分区副本数 >= 2 + ISR最小副本数 >= 2
)
相同主键的消息提交时,Broker只会持久化一条。
PID
是kafka每次重启都会分配一个新的;Partition
标识分区号;Sequence Number
是单调自增的。只能保证的是在单分区单会话内不重复
enable.idempotence
,默认为true
,false
为关闭PID
就会变化,此时就无法保证数据不重复max.in.flight.requests.per.connection=1(不需要考虑是否开启幂等性)
max.in.flight.requests.per.connection=1
max.in.flight.requests.per.connection
需要设置小于等于5broker.id
datas
和logs
)vim topics-to-move.json
{
"topics": [
"topic": "demo1"
],
"version": 1
}
/opt/module/kafka/bin/kafka-reassign-partitions.sh --bootstrap-server first-node:9092 --topics-to-move-json-file topics-to-move.json --broker-list "0,1,2,3" --generate
vim increase-replication-factor.json
Proposed partition reassignment configuration
{"version":1, "partitions":[{"topics":"demo1", "partition":0, "replicase":[3, 1], "log_dirs":["any", "any"]}, {"topics":"demo1", "partition":1, "replicase":[0, 2], "log_dirs":["any", "any"]}, {"topics":"demo1", "partition":2, "replicase":[1, 3], "log_dirs":["any", "any"]}, {"topics":"demo1", "partition":3, "replicase":[2, 0], "log_dirs":["any", "any"]}, {"topics":"demo1", "partition":4, "replicase":[3, 2], "log_dirs":["any", "any"]}, {"topics":"demo1", "partition":5, "replicase":[0, 3], "log_dirs":["any", "any"]}, {"topics":"demo1", "partition":6, "replicase":[1, 0], "log_dirs":["any", "any"]}]}
/opt/module/kafka/bin/kafka-reassign-partitions.sh --bootstrap-server first-node:9092 --reassignment-json-file increase-replication-factor.json --execute
Successfully started...
即表示执行成功/opt/module/kafka/bin/kafka-reassign-partitions.sh --bootstrap-server first-node:9092 --reassignment-json-file increase-replication-factor.json --verify
1.创建一个要均衡的主题:vim topics-move.json
{
"topics": [
"topic": "demo1"
],
"version": 1
}
2.创建执行计划
/opt/module/kafka/bin/kafka-reassign-partitions.sh --bootstrap-server first-node:9092 --topics-to-move-json-file topics-to-move.json --broker-list "0,1,2" --generate
3.创建副本存储计划(所有副本存储在broker0、broker1、broker2中):vim increase-replication-factor.json
{"version":1, "partitions":[{"topics":"demo1", "partition":0, "replicase":[2, 0, 1], "log_dirs":["any", "any", "any"]}, {"topics":"demo1", "partition":1, "replicase":[0, 1, 2], "log_dirs":["any", "any", "any"]}, {"topics":"demo1", "partition":2, "replicase":[1, 2, 0], "log_dirs":["any", "any", "any"]}]}
4.执行副本存储计划
/opt/module/kafka/bin/kafka-reassign-partitions.sh --bootstrap-server first-node:9092 --reassignment-json-file increase-replication-factor.json --execute
Successfully started...
即表示执行成功5.验证副本存储计划
/opt/module/kafka/bin/kafka-reassign-partitions.sh --bootstrap-server first-node:9092 --reassignment-json-file increase-replication-factor.json --verify
kafka-server-stop.sh
Leader
和Follower
。Kafka生产者只会把数据发往Leader
,然后Follower
找Leader
进行数据同步AR(Assigned Repllicas)
AR = ISR + OSR
ISR
:标识和Leader保持同步的Follwer
集合,如果Follower长时间未向Leader发送通信请求或同步数据,则该Follower将被踢出ISR队列。该时间的阈值由replica.lag.time.max.ms
参数设定,默认30s。Leader发生故障后,就会从ISR中选出新的LeaderOSR
:标识Follower和Leader副本同步时,延时过多的副本Controller
会被选举为Controller Leader
,负责管理集群broker的上下线
,所有topic
的分区副本分配和Leader
选举等工作Controller
的信息同步工作是依赖于Zookeeper
的Broker
时,在zookeeper
中/kafka/brokers/ids
下进行注册Broker
中的Controller
抢先注册zookeeper
中的/kafka/controller
节点,谁抢到了谁就是controller leader
controller leader
监听/kafka/broker/ids
的变化Broker
节点启动过半后,Controller
开始选举Leader
Controller
选举出Leader
后,开始向zookeeper
的/kafka/brokers/topics/demo1/partitions/0/state
节点写入信息Broker
上的Controller
从zookeeper
上同步相关信息,防止成为leader的Controller
意外挂掉Broker1
,此时如果Broker1
上的Leader
意外挂了Controller
监测到/kafka/brokers/ids
上的变化Controller
从/kafka/brokers/topics/demo1/partitions/0/state
节点获取ISR
信息/kafka/brokers/topics/demo1/partitions/0/state
节点信息(主要是leader
,isr
)demo2
,将该topic的所有副本都存储到broker0
和broker1
两台服务器上demo2
主题kafka-topics.sh -bootstrap-server first-node:9092 --create --topic demo2 --partitions 4 --replication-factor 2
kafka-topics.sh -bootstrap-server first-node:9092 --describe --topic demo2
Topic: demo2 Topicld: -tfY5h@3Rg236b6sooxs7g PartitionCount: 4 ReplicationFactor: 2 configs; segment. bytes=1073741824
Topic: demo2 Partition: 0 Leader: 2 Replicas: 2,0 Isr: 2,0
Topic: demo2 Partition: 1 Leader: 3 Replicas: 3,2 Isr: 3,2
Topic: demo2 Partition: 2 Leader: 1 Replicas: 1,3 Isr: 1,3
Topic: demo2 Partition: 3 Leader: 0 Replicas: 0,1 Isr: 0,1
vim increase-replication-factor.json
{
"version": 1,
"partitions": [
{"topic": "demo2", "partition": 0, "replicas": [0, 1]},
{"topic": "demo2", "partition": 1, "replicas": [0, 1]},
{"topic": "demo2", "partition": 2, "replicas": [0, 1]},
{"topic": "demo2", "partition": 3, "replicas": [0, 1]},
]
}
kafka-reassign-partitions.sh -bootstrap-server first-node:9092 --reassignment-json-file increase-replication-factor.json --execute
kafka-reassign-partitions.sh -bootstrap-server first-node:9092 --reassignment-json-file increase-replication-factor.json --verify
kafka-topics.sh -bootstrap-server first-node:9092 --describe --topic demo2
自动把Leader Partition平均分散在各个机器上
,来保证每台机器的读写吞吐量都是均匀的。但是如果某些broker宕机,会导致Leader Partition过于集中在其他少部分几台broker上
,这会导致少数几台broker的读写请求压力过高,其他宕机broker重启后都是follower partition
,读写请求很低,造成负载不均衡。auto.leader.rebalance.enable
:默认是true
。自动平衡(如果机器性能不一致,且做了手动调整分区存储,那么不建议将其设置为true
)
leader.imbalance.per.broker.percentage
:每个broker允许的不平衡leader的比率,默认10%
,如果超过这个值,控制器会触发leader
的平衡leader.imbalance.check.interval.seconds
:检查leader负载是否平衡的间隔时间。创建topic
kafka-topics.sh -bootstrap-server first-node:9092 --create --topic demo3 --partitions 3 --replication-factor 1
手动增加副本存储,创建副本存储计划(所有副本都指定存储在broker0、broker1、broker2中):vim increase-replication-factor.json
{
"version": 1,
"partitions": [
{"topic": "demo3", "partition": 0, "replicas": [0, 1, 2]},
{"topic": "demo3", "partition": 1, "replicas": [0, 1, 2]},
{"topic": "demo3", "partition": 2, "replicas": [0, 1, 2]}
]
}
执行副本存储计划
kafka-reassign-partitions.sh -bootstrap-server first-node:9092 --reassignment-json-file increase-replication-factor.json --execute
Topic是逻辑上的概念,而partition是物理上的概念,每个partition对应一个log文件
,该log文件中存储的就是producer生产的数据。producer生产的数据会被不断追加到该log文件末端
,为防止log文件过大导致数据定位效率低下,kafka采取了分片和索引机制,将每个partition分为多个segment
,每个segment
包括:.index
偏移量索引文件,.log
日志文件,和.timeindex
时间戳索引等文件。这些文件位于一个文件夹下,该文件夹的命名规则为:topic名称+分区号,例如:demo1-0
。
消费者消费完成后不会删除数据,数据默认保存7天,由timeindex
来计算
查看.log
文件
kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.index Dumping ./00000000000000000000.index
Starting offset: 0
baseOffset: 0 lastOffset: 0 count: 1 baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: 0 isTransactional: false isControl: false position: 75 CreateTime:1670918886067 size: 75 magic: 2 compresscodec: none crc: 73416945 isvalid: true
查看.index
文件
kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.log Dumping ./00000000000000000000.log
offset: 0 position: 0
默认的日志保存时间为7天
,可以通过调整如下参数修改保存时间
log.retention.hours
,最低优先级小时,默认7天log.retention.minutes
,分钟log.retention.ms
,最高优先级毫秒log.retention.check.interval.ms
,负责设置检查周期,默认5分钟delete
和compact
两种delete
:日志删除,将过期数据删除
log.cleanup.policy=delete
:所有数据启用删除策略segment
中有一部分数据过期,一部分数据没有过期,怎么处理?
基于时间
:默认打开,以segment中所有记录中的最大时间戳作为该文件时间戳,如果最大时间戳距今超过7天,就直接删除基于大小
:默认关闭,超过设置的所有日志总大小 ,删除最早的segment
log.retention.bytes
默认等于-1
,标识无穷大compact
:日志压缩(对于相同的key的不同value值,只保留最新的,类似于python字典中的update方法)
offset
可能是不连续的,当从这些offset中消费消息时,将会拿到比这个offset大的offset对应的消息,实际上会拿到offset为7的消息,并从这个位置开始消费。稀疏索引
,可以快速定位药消费的数据一直追加
到文件末端,为顺序写。官方测试结果:顺序写能达到600M/s,而随机写只有100K/s。这与磁盘的机械结构有关,顺序写之所以快,是因为其省去了大量磁头寻址的时间零拷贝
:kafka的数据加工处理操做交由kafka生产者和kafka消费者处理,kafka broker应用层不关心存储的数据,所以就不用走应用层,传输效率高。页缓存(PageCache)
:kafka重度依赖底层操做系统提供的PageCache
功能,当上层有写操作时,操做系统只是将数据写入PageCache
。当度操做发生时,先从PageCache
中查找,如果找不到,再去磁盘中读取。实际上PageCache
是把尽可能多的空闲内存都当作了磁盘使用pull(拉)模式
:kafka采用
push(推)模式
:
50m/s
,而Consumer的消费能力如果只有20m/s
,则来不及处理每个消费者可以从多个分区拉取数据,也可以多个消费者从同一个分区消费数据
如果消费者建立分组,那么分组内的多个消费者不可以消费一个分区的数据
__consumer_offsets
,来记录主题内消费偏移信息,即使某一个消费者挂了,其他消费者依然可以从未消费的位置开始消费Consumer Group(CG)
:消费者组由多个consumer组成,形成一个消费者组的条件,是所有消费者的gropuid
相同
消费者组内的每个消费者负责不同分区的数据,一个分区只能由组内的一个消费者消费
消费者组之间互不影响。
所有的消费者都属于某个消费者组。即消费者组是逻辑上的一个订阅者
coordinator
:辅助实现消费者组的初始化和分区的分配。
coordinator
节点的选择 = groupid的hashcode值%50
__consumer_offsets的分区数量 = 50
__consumer_offsets
主题的1号分区,在哪个broker上,就选择这个节点的coordinator
作为这点消费者组的老大,消费者组下的所有消费者提交offset的时候就往这个分区去提交offsetconsumer group
中有多个consumer
组成,一个topic
有多个partition
组成,现在的问题是,到底是由哪个consumer来消费哪个partition的数据
Range
RoundRobin
Sticky
CooperativeSticky
partition.assignment.stratge
,修改分区的配置策略。默认策略是:Range + CooperativeSticky
,kafka可以同时使用多个分区配置策略。Range是对每个topic而言
分区按照序号进行排序
,并对消费者按照字母顺序排序
0,1,2,3,4,5,6
,消费者排序后:C0,C1,C2
partitions数 / consumer数
来决定每个消费者应该消费几个分区。如果除不尽,那么前面几个消费者会多消费一个分区
。
注意
:如果只是针对一个topic而言,C0消费者多消费一个分区影响不是很大,但是如果有N多个topic,那么针对每个topic消费者C0都将多消费一个分区,topic越多,C0消费的分区会比其他消费者明显多消费N个分区,容易产生数据倾斜
。轮询
算法来分配partition给到各个消费者触发再平衡
,此时按照剩余的消费者进行range策略
黏性的
,即再执行一次新的分配前,考虑上一次分配的结果,尽量少的调整分配的变动,可以减少大量的开销随机均分
分区0、1
分区4、5、6
分区2、3
分区0、1、4、6
分区2、3、5
分区触发再平衡
,此时按照剩余的消费者进行range策略
0、1、4、6
分区2、3、5
分区内置的topic
中,该topic为__consumer_offsets
__consumer_offsets
主题里面采用了key
和value
的方式存储数据,key:group.id + topic + 分区号
,value
就是当前offset
的值。每隔一段时间,kafka内部会对这个topic
进行compact
(压缩:覆盖更新),也就是每个roup.id + topic + 分区号就保留最新数据
__consumer_offsets
这个系统主题/opt/module/kafka/config/consumer.properties
中添加配置exclude.internal.topics=false
,默认为true,表示不能消费系统主题,为了查看系统主题数据,所以需要修改为falsexsync分发脚本
__consumer_offsets
主题数据kafka-console-consumer.sh --bootstrap-server first-node:9092 --topic __consumer_offsets --consumer.config config/consumer.properties --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" --from-beginning
offset
十分便利,但由于其是基于时间提交的,开发人员难以把握offset
提交的时机,因此kafka还提供了手动提交offset
的api
offset
的方法有两种,分别是:commitSync(同步提交)
和commitAsync(异步提交)
。两者的相同点是,都会将本次提交的一批数据最高的偏移量提交
;不同点是,同步提交阻塞当前线程
,一直到提交成功,并且会自动失败尝试(由不可控因素导致,也会出现提交失败);而异步提交没有失败重试机制,故有可能提交失败
。
auto.offset.reset=earliest|latest|none
默认是latest
漏消费
:先提交offset
后消费,有可能会造成数据的漏消费(手动提交offset)
解决方法:消费者事务
Consumer
端的精准一次性消费,那么需要kafka消费端将消费过程和提交offset过程做原子绑定
。此时我们需要将kafka的offset保存到支持事务的自定义介质(比如Mysql)。增Topic的分区数
,并且同时提升消费组的消费者数量,消费者数=分区数
。(两者缺一不可)提高每批次拉取数据的数量
。批次拉取数量过少(拉取数据/处理时间 < 生产速度),使处理的数据小于生产的数据,也会造成数据积压