消息持久化及其缓存
磁盘性能:在传统的磁盘写入很慢,因为它使用随机写入 50k/s(6 个 7200 转的 sata 硬盘组成的 raid-5),但是线性写入速度有 300ms/s 的速度, 所以 kafka 利用线性写入的方式。 线性写入:将数据调用操作系统文件接口写到文件系统里面去这样就缓存到操作系统的页面缓存中,然后传统意思来说将其 flush 一下到磁盘中,但是 kafka 并没有这样,而是保存在页面缓存中(相当于放在内存当中)并没有进行 flush 操作,这样他就会提供比较高的读的性能,下次读就从内核页面缓存中读数据,但是内存中存储数量不是无限大的, 所以我们配置参数(每当接收到 N 条信息或者每过 M 秒),进行一个 flush 操作,从而可以为系统硬件崩溃时“处于危险之中”的数据在量上加个上限。
kafka 的缓存不是在内存中保存尽可能多的数据并在需要时将这些数刷新到文件系统,而是做完全相反的事情,将所有的数据立即写入文件系统中的持久化的日志中,但不进行刷刷新数据的调用,实际这么做意味着数据被传输到 os 内核的页面缓存中去了,随后在根据配置刷新到硬盘。
持久化常量时间
以往的消息系统元数据的持久化数据结构往往采用 BTree。BTree 是目前最常用额数据结构,在消息系统中它可以用来广泛支持多种不同的事物性活非事务性的语义,他的确带来了一个非常高的处理开销,Btree 的运算时间的复杂度为 0(LOG n),Btree 需要一种非常负责的页面级或者行级锁才能避免在每次操作时锁定整颗树。实现这种机器就要为行级锁定付出非常昂贵的代价,负责就必须所有的读取操作进行串行化(serialize).因为对磁盘寻道操作的高度依赖,就不太可能高效的从驱动器密码的提高中获得改善,因而就不得不使用容量较小(<100GB)转速较快的 sas 驱动,以维持一种比较合理的数据与寻道容量之比。kafka 的设计是什么样的呢?kafka 利用按照通常的日志解决方案的样子构建,只是简单的文件读取和简单地向文件中追加内容。当然相比的劣势在于不支持 Btree 这样事物和非事物的语义支持,但是优势在于其所有的操作夫妻度都是 0(1),读取操作并不需要阻止写入操作,而且反之亦然。这样做显然有性能优势,因为性能完全同数据大小之间脱离了关系。能够存取到几乎无限大的磁盘空间而无须付出性能代价,意味着,我们可以提供其他消息系统并不常见的功能,例如在 kafka 中,消息在使用完全后并没有立即删除,而是会将这些消息保存相当长一段时间,这样可以利用offset 功能进行多次消费, 比如说 可以设置从某个 offset id 往下进行消费,当然一般只有是消费失败才回这么做。
传统的消息发送:
a、操作系统将数据从磁盘中读取到内核空间里的页面缓存。
b、应用程序将数据从内核空间读入到用户空间的缓冲区。
c、应用程序将读到的数据写会内核空间并放入 socket 的缓冲区
d、操作系统将数据从 socket 的缓冲区拷贝到 NIC(网络接口卡,即网卡) 的缓冲区,自此数据才能通过网络发送出去,这样效率显然很低,因为里面涉及到 4 此拷贝,2 次系统调用。
kafka 的信息发送是如何的呢?
kafka 使用 sendfile(0 拷贝)就可以避免这些重复的拷贝操作,让 os 直接将数据从页面缓存发送到网络中,其中只需最后一步中的将数据拷贝到 NIC 的缓冲区。
kafka 采用全文所属的 sendfile 拷贝优化方案,数据只需要拷贝到页面缓存中一次,然后每次发送给使用者时都对他重复使用即可,一次拷贝发给多个消费者。
1、巡检
1.1、查看状态
1.2、查看流量
1.3、查看分区
2、参数调优
2.1、broker
broker.id=2
listeners=SASL_PLAINTEXT://xxx.xxx.xxx.xxx:9092
可以按如下配置将自动生成 brokeid 自动识别 host.name
#broker.id=2
listeners=SASL_PLAINTEXT://:9092
zookeeper.connect=10.161.17.71:2181,10.161.17.72:2181,10.161.17.73:2181/kafka
2.2、连接zookeeper 超时时间
zookeeper.connection.timeout.ms=60000
zookeeper.session.timeout.ms=60000
2.3、broker 优雅下线参数
controlled.shutdown.enable=true
开启的话,broker 在关闭时会向 controller 报告自己关闭,这样 controller 可以对 broker 的下线及时做一些操作,比如 partition 的重新选举、分区副本的关闭、通知其他的 broker 元数据变动等
2.4、关闭自动创建topic 的选项
auto.create.topics.enable=false
2.5、线程参数
1.负责写磁盘的线程数
num.io.threads=13
整个参数值要占总核数的 50%,比如你的服务器的是 24 核,要给 13 核用于写磁盘
2.副本拉取线程数
num.replica.fetchers=4
这个参数占总核数的 50%的 1/3
3.数据传输线程数
num.network.threads=7
这个参数占总核数的 50%的 2/3
2.6、通讯参数
产线环境可以适当调大
socket.receive.buffer.bytes=1048576
socket.send.buffer.bytes=1048576
2.7、队列参数
queued.max.requests=10000
这个参数是指定用于缓存网络请求的队列的最大容量,这个队列达到上限之后将不再接收新请求,产线环境适当调大
2.8、其他参数
auto.leader.rebalance.enable=true
leader.imbalance.per.broker.percentage=10
leader.imbalance.check.interval.seconds=3600
#副本拉取的最小大小
1mb replica.fetch.min.bytes=1
副 本 拉 取 的 最 大 大 小 20mb
replica.fetch.max.bytes=20971520
#多长时间拉取一次副本
replica.fetch.wait.max.ms=500
超 过 多 长 时 间 副 本 退 出
isr replica.socket.timeout.ms=60000
#replica.fetch.wait.max.ms=1000
#缓存大小
replica.socket.receive.buffer.bytes=131072
#每当 producer 写入 10000 条消息时,刷数据到磁盘
log.flush.interval.messages=10000
#每间隔 1 秒钟时间,刷数据到磁盘
log.flush.interval.ms=1000
3、运维
3.1、常用命令
1.创建 topic
./kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 10 --topic t-mytopic
说明:
--replication-factor 指定了复制因子,即每个分区在集群中有 3 份数据
--partitions 指定 topic 的分区数
2.查看 topic 详情
./kafka-topics.sh --zookeeper localhost:2181 --topic t-mytopic --describe
打印如下:
Topic:t-mytopic PartitionCount:10 ReplicationFactor:3 Configs: Topic: t-mytopic Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 0,1,2
Topic: t-mytopic Partition: 1 Leader: 2 Replicas: 2,0,1 Isr: 0,1,2
Topic: t-mytopic Partition: 2 Leader: 0 Replicas: 0,1,2 Isr: 0,1,2
Topic: t-mytopic Partition: 3 Leader: 1 Replicas: 1,0,2 Isr: 0,1,2
Topic: t-mytopic Partition: 4 Leader: 2 Replicas: 2,1,0 Isr: 0,1,2
Topic: t-mytopic Partition: 5 Leader: 0 Replicas: 0,2,1 Isr: 0,1,2
Topic: t-mytopic Partition: 6 Leader: 1 Replicas: 1,2,0 Isr: 0,1,2
Topic: t-mytopic Partition: 7 Leader: 2 Replicas: 2,0,1 Isr: 0,1,2
Topic: t-mytopic Partition: 8 Leader: 0 Replicas: 0,1,2 Isr: 0,1,2
Topic: t-mytopic Partition: 9 Leader: 1 Replicas: 1,0,2 Isr: 0,1,2
说明;
ReplicationFactor 表示复制因子,即每个分区有几份数据
Leader 分区的主在那个 broker 上
Replicas 表示分区数据分布在哪些 broker 上
Isr 表示可立即作为分区的主的 broker,即标识 3 分数据已经保持一致了。
3.查看消费者列表
./kafka-consumer-groups.sh --bootstrap-server hadoop1:9092 --list
查看某个消费者消费的位置
./kafka-consumer-groups.sh --bootstrap-server hadoop1:9092 --describe --group console-consumer-29885
打印信息如下:
`TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST CLIENT-ID
t-mytopic 2 0 0 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1 t-mytopic 8 0 0 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1 t-mytopic 7 0 0 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1 t-mytopic 1 0 0 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1 t-mytopic 5 0 0 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1 t-mytopic 6 1 1 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1 t-mytopic 0 0 0 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1 t-mytopic 4 0 0 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1 t-mytopic 3 0 0 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1 t-mytopic 9 0 0 0
consumer-1-38ad4fcc-0726-484d-8b81-6a67213d459c /10.191.21.9 consumer-1`
说明:
Group 消费者组
Topic 曾经消费或正在消费的 topic
pid 分区编号
Offset 消费者组在分区上消费的位置
logSize 分区上的累计消息条数
Lag 还有多少条未消费,当为 0 时代表消费完,此时 Offset 列跟 logSize 列相等。
Owner 如果为 none 标识消费者组没有消费线程对分区进行消费,如果有线程消费这里将显示线程名称。
4.模拟生产者
./kafka-console-producer.sh --broker-list hadoop1:9092 --topic t-mytopic
5.模拟消费者
./kafka-console-consumer.sh --bootstrap-server hadoop1:9092 --from-beginning --topic t-mytopic
3.2、扩缩容
同HDFS扩缩容
3.3、手动降低副本
1.编写分区分配 json 文件,如下内容保存成 replication-factor.json 文件
"version": 1, "partitions": [
{
"topic": "t-mytopic", "partition": 0, "replicas": [
65,
63
]
},
{
"topic": "t-mytopic", "partition": 3, "replicas": [
64,
65
]
},
{
"topic": "t-mytopic", "partition": 7, "replicas": [
65,
63
]
},
{
"topic": "t-mytopic", "partition": 6, "replicas": [
65,
63
]
},
{
"topic": "t-mytopic", "partition": 2, "replicas": [
63,
65
]
},
{
"topic": "t-mytopic", "partition": 5, "replicas": [
63,
65
]
},
{
"topic": "t-mytopic", "partition": 9, "replicas": [
64,
63
]
},
{
"topic": "t-mytopic", "partition": 8,
"replicas": [ 63,
64
]
},
{
"topic": "t-mytopic", "partition": 4, "replicas": [
64,
63
]
},
{
"topic": "t-mytopic", "partition": 1, "replicas": [
65,
64
]
}
]
}
2.执行如下命令开发分配分区位置
./kafka-reassign-partitions.sh --zookeeper localhost:2181 --reassignment-json-file replication-factor.json --execute
3.4、分区手动分配
将 mytopic1,mytopic2 两个主题的所有分区都迁移到新的 broker 机器5,6 上。最后,mytopic1,mytopic2 两个主题的所有分区都厚在 brokers 5,6 上。
{"topics": [{"topic": "mytopic1"}, {"topic": "mytopic2"}], "version":1 }
bin/kafka-reassign-partitions.sh --zookeeper localhost:2181 --topics-to-move-json-file topics-to-move.json --broker-list "5,6" --generate
Current partition replica assignment
{
"version": 1, "partitions": [
{
"topic": "mytopic1", "partition": 2, "replicas": [
1,
2
]
},
{
"topic": "mytopic1", "partition": 0, "replicas": [
3,
4
]
},
{
"topic": "mytopic2", "partition": 2, "replicas": [
1,
2
]
},
{
"topic": "mytopic2", "partition": 0, "replicas": [
3,
4
]
},
{
"topic": "mytopic1", "partition": 1, "replicas": [
2,
3
]
},
{
"topic": "mytopic2", "partition": 1, "replicas": [
2,
3
]
}
]
}
Proposed partition reassignment configuration
{
"version": 1, "partitions": [
{
"topic": "mytopic1", "partition": 2, "replicas": [
5,
6
]
},
{
"topic": "mytopic1", "partition": 0, "replicas": [
5,
6
]
},
{
"topic": "mytopic2", "partition": 2, "replicas": [
5,
6
]
},
{
"topic": "mytopic2", "partition": 0, "replicas": [
5,
6
]
},
{
"topic": "mytopic1", "partition": 1, "replicas": [
5,
6
]
},
{
"topic": "mytopic2", "partition": 1, "replicas": [
5,
6
]
}
]
}
工具生成了一个把主题mytopic1,mytopic2 所有分区迁移到brokers 5,6 上的计划。注意,分区迁移还没有开始。它只是告诉你当前分配计划和新计划的提议。为了防止万一需要回滚,新的计划应该保存起来。
新的调整计划应该保存成一个 json 文件(如:expand-cluster-reassignment.json),并以–execute 选项的方式,如下:
]
}
]
}
Save this to use as the --reassignment-json-file option during rollback Successfully started reassignment of partitions
{
"version": 1, "partitions": [
{
"topic": "mytopic1", "partition": 2, "replicas": [
5,
6
]
},
{
"topic": "mytopic1", "partition": 0, "replicas": [
5,
6
]
},
{
"topic": "mytopic2", "partition": 2, "replicas": [
5,
6
]
},
{
"topic": "mytopic2", "partition": 0, "replicas": [
5,
6
]
},
{
"topic": "mytopic1", "partition": 1, "replicas": [
5,
6
]
},
{
"topic": "mytopic2", "partition": 1, "replicas": [
5,
6
]
}
]
}
执行验证: –verify
bin/kafka-reassign-partitions.sh --zookeeper localhost:2181 --reassignment-json-file custom-reassignment.json --verify
Status of partition reassignment: Reassignment of partition [mytopic1,0]
completed successfully
Reassignment of partition [mytopic2,1] completed successfully
3.5、磁盘规划
1.评估数据量:要求研发提前评估 topic 一个周期全量的数据大小。
2.计算磁盘总存储:如一块盘 825g,一个节点 20 快盘,10 个节点。 那么磁盘总存储就是 165000g。
3.预估实际数据存储占比:topic 一个周期全量数据大小占磁盘总存储的百分比,超过百分之六十,即要求研发减少存储周期。
4.计算磁盘总块数:一个节点 20 快盘,10 个节点,总磁盘块数 200 个。
5.合理预分区:分区数量为磁盘总数的整数倍。如所有的 topic 总数据量为 50000g,磁盘个数为 200,那么就可以设置总分区数为 200/400/600.具体多少分区数视业务决定。若分区数为 400,那么一个分区的大小约 125g。例如某一个 topic:cbss001 的预估数据量是 210g,那么通过计算可以将其分成两个分区。这样根据 kafka 副本落盘策略,各个主机磁盘就能保证最大限度的存储均衡。
6.如果我们建立了一个非常大的 topic,那么此 topic 的分区数量应该是磁盘数的整数倍;如果说我们还有建立一个小的 topic,那么此 topic 的每个分区大小应该跟之前的大的 topic 分区的大小保持一致。
4、排障
4.1、常见故障
1.坏盘会导致节点宕掉,及时更换坏盘,重启节点即可。
2.内存溢出,其会导致节点副本不能上线isr。
3.进程\文件数限制也会造成节点报错,后续调优中会给出优化参数。
4.flower副本不能及时同步leader副本,同步超时导致副本下线isr。
5.消费offset越界,这种情况首先重启节点,若还是报错,则找到该offset越界的分区,删除几条message,再次查看。知道不报错为止。
4.2、sparkExceptionCouldn’t find leader for set(1)
【现象】
生产人员执行spark on yarn 任务,报错找不到leader: SparkException:Couldn’t find leader for set
【排查处理】
检查kafka相关组件进程
首先查看组件进程是否alive
1)kafka 相关组件分布:
10.161.XX.XX:kafka 的broker1、kafka 的zookeeper。
10.161.XX.XX:kafka 的broker2。
这套zookeeper 只有 1 个,为standalone 模式。
2)检查kafka 进程:用kafka 用户执行jps 查看
检查zookeeper 进程:
①在 10.161.XX 上用root 用户执行jps 查看
②在 10.161.XX.XX 主机用 hadoop 用户执行zkServer.sh status
正常输出如下:
如果上述进程已dead,用其相应的查看用户启动即可:
zookeeper:用root 用户执行
/opt/beh/core/zookeeper/bin/zkServer.sh zkServer.sh start
kafka:用kafka 用户执行
**nohup /opt/beh/core/kafka/bin/kafka-server-start.sh /opt/beh/core/kafka/config/server.properties & **如果kafka 启动失败,去ouhup.out 里看输出。
3)检查kafka 和zookeeper 的连接
在确定kafka 和zookeeper 的进程都alive,但生产侧报错依旧的情况下,几乎可以判定kafka 和zookeeper 失联了。
在 10.161.XX.XX 上用 hadoop 用户执行zkCli.sh,进入zookeeper 的shell 环境。
执行ls /brokeRegionServer/ids,查看和 zookeeper 连接的kafka 进程:
正常情况下,下一行的输出应该是[1,2]或者[2,1]
如果有某个kafka 和zookeeper 失联了,其 broker 的id 将不会显示,例如:
这说明id 为 2 的kafka 已经和zookeeper 失联了,去10.161.XX.XX 主机重启kafka 即可。
重启方法:先用kafka 用户kill -9 kafka 的进程,然后执行**nohup /opt/beh/core/kafka/bin/kafka-server-start.sh /opt/beh/core/kafka/config/server.properties & **。
确定 10.161.XX.XX 上的kafka 进程alive 后,返回10.161.XX.XX 上用 hadoop 用户执行zkCli.sh,再次检查zookeeper 和kafka 的连接。
若有收获,就点个赞吧