前段时间收到某个 Kafka 集群的生产客户端反馈发送消息耗时很高,因而花了一段时间去排查这个问题,最后该集群进行扩容,因为某些主题的当前数据量实在太大,在对这些主题迁移过程当中话费了很长一段时间,不过这个过程还算顺利,由于在迁移过程当中也作足了各方面的调研,包括分区重平衡过程当中对客户端的影响,以及对整个集群的性能影响等,特此将这个过程总结一下,也为双十一打了一剂强心剂。json
接到用户的反馈后,我用脚本测试了一遍,并对比了另一个正常的 Kafka 集群,发现耗时确实很高,接下来后端
通过排查,发现有客户端在频繁断开与集群节点的链接,发现日志频繁打印以下内容:bash
Attempting to send response via channel for which there is no open connection, connection id xxx(kafka.network.Processor)
定位到源码位置:运维
kafka.network.Processor#sendResponse:性能
看源码注释,是远程链接关闭了或者空闲时间太长了的意思,找到具体客户端负责人,经询问后,这是大数据 Spark 集群的节点。测试
从以上日志看出,Spark 集群的某个消费组 OrderDeliveryTypeCnt,居然发生了近 4 万次重平衡操做,这显然就是一个不正常的事件,Kafka 消费组发生重平衡的条件有如下几个:大数据
很显然第 二、3 点都没有发生,那么能够判定,这是 Spark集群节点频繁断开与kafka的链接致使消费组成员发生变动,致使消费组发生重平滑。spa
那为何 Spark 集群会产生频繁断开重连呢?.net
查看 Spark 集群用的 Kafka 版本仍是 0.10.1.1 版本,而 Kafka 集群的版本为 2.2.1,一开始觉得是版本兼容问题,接着数据智能部的小伙伴将 Spark 集群链接到某个版本为 0.11.1.1 的 Kafka 集群,使用 8 个 Spark 任务消费进行消费,一样发现了链接断开的问题。说明此问题是因为 Spark 内部消费 Kafka 机制致使的,和 kafka 版本关系不大。3d
通过几番跟大数据的人员讨论,这个频繁重平衡貌似是 Spark 2.3 版本内部机制致使的,Spark 2.4 版本没有这个问题存在。
因为这个频繁断开重连,并非开发人员开发过程当中致使的,考虑到双十一临近,不能贸然升级改动项目,那么如今最好的方案就是对集群进行水平扩展,增长集群的负载能力,并对专门的主题进行分区重分配。
目前集群一共有 6 个节点,扩容以 50% 为基准,那么须要在准备 3 个节点,在运维准备好机器而且将其加入到集群中后,接下来就要准备对主题进行分区重分配的策略文件了。
在执行分区重分配的过程当中,对集群的影响主要有两点:
针对以上两点,第 1 点能够在晚间进行(太苦逼了,记得有个主题数据迁移进行了将近5小时),针对第二点,我想到了两个方案:
第一个方案理论上是对客户端影响最小的,把整个分配方案分红了两个步骤,也就是将对集群的带宽资源与客户端的影响分开了,对过程可控性更高了,但问题来了,集群中的某些主题,有 64 个分区,副本因子为 3,副本一共有 192 个,你须要保持原有分区 Leader 位置不变的状况下,去手动均衡其他副本,这个考验难度真的太大了,稍微有一点误差,就会形成副本不均衡。
所以我特地去看了分区重分配的源码,并对其过程进行了进一步分析,发现分配重分配的步骤是将分区原有的副本与新分配的副本的集合,组成一个分区副本集合,新分配的副本努力追上 Leader 的位移,最终加入 ISR,待所有副本都加入 ISR 以后,就会进行分区 Leader 选举,选举完后就会将原有的副本删除,具体细节我会单独写一篇文章。
根据以上重分配的步骤,意味着在数据进行过程当中不会发生客户端阻塞,由于期间 Leader 并无发生变动,在数据迁移完成进行 Leader 选举时才会,但影响不大,针对这点影响我特地用脚本测试了一下:
能够发现,在发送过程当中,若是 Leader 发生了变动,生产者会及时拉取最新的元数据,并从新进行消息发送。
针对以上的分析与测试,咱们决定采起第二种方案,具体步骤以下:
auto.leader.rebalance.enable=true
,所以会自动执行 Preferred Leader 选举,默认时间间隔为 300 秒,期间须要观察 Preferred Leader 选举情况。对于新增的 Broker,Kafka 是不会自动地分配已有主题的负载,即不会将主题的分区分配到新增的 Broker,但咱们能够经过 Kafka 提供的 API 对主题分区进行重分配操做,具体操做以下:
echo '{"version":1,"topics":[{"topic":"sjzn_spark_binlog_order_topic"}]}' > sjzn_spark_binlog_order_topic.json
bin/kafka-reassign-partitions.sh --zookeeper --zookeeper xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181 --topics-to-move-json-file sjzn_spark_binlog_order_topic.json --broker-list "0,1,2,3,4,5,6,7,8" --generate
因为主题的有64个分区,每一个分区3个副本,生成的分配数据仍是挺大的,这里就不一一贴出来了
echo '{"version":1,"partitions":[{"topic":"sjzn_spark_binlog_order_topic","partition":59,"replicas":[4,8,0],"log_dirs":["any","any","any"]} ......' > sjzn_spark_binlog_order_topic_reassignment.json
bin/kafka-reassign-partitions.sh --zookeeper xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181 --reassignment-json-file sjzn_spark_binlog_order_topic_reassignment.json --execute
bin/kafka-reassign-partitions.sh --zookeeper xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181,xxx.xxx.xx.xxx:2181 --reassignment-json-file sjzn_spark_order_unique_topic_resign.json --verify
因为该主题存在的数据量特别大,整个重分配过程须要维持了好几个小时:
在它进行数据迁移过程当中,我特地去 kafka-manage 控制台观察了各分区数据的变更状况:
从控制台可看出,各分区的副本数目基本都增长了,这也印证了分区当前的副本数等于原有的副本加上新分配的副本的集合,新分配的副本集合目前还没追上 Leader 的位移,所以没有加入 ISR 列表。
有没有注意到一点,此时各分区的 Leader 都不在 Preferred Leader 中,所以后续等待新分配的副本追上 ISR 后,会进行新一轮的 Preferred Leader 选举,选举的细节实现我会单独写一篇文章去分析,敬请期待。
过一段时间后,发现位移已经改变了:
从这点也印证了在分区重分配过程当中,只要 Leader 没有发生变动,客户端是能够持续发送消息给分区 Leader 的。
从上图可看出,新分配的副本追上 Leader 的位移后,就会加入 ISR 列表中。
如今去看看集群带宽负载状况:
从上图中可看出,在迁移过程当中,新分配的副本不断地从 Leader 拉取数据,占用了集群带宽。
主题各分区重分配完成后的副本状况:
从以上图中可看出,各分区的新分配的副本都已经所有在 ISR 列表中了,而且将旧分配的副本删除,通过 Preferred Leader 选举以后,各分区新分配副本的 Preferred Leader 大多数成为了该分区 leader。
更多精彩文章请关注做者维护的公众号「后端进阶」,这是一个专一后端相关技术的公众号。
关注公众号并回复「后端」免费领取后端相关电子书籍。
欢迎分享,转载请保留出处。