kafka高可用失败问题 3broker,单杀一个broker就不能消费的问题探讨

solo 发表于: 2018-01-24   最后更新时间: 2021-09-16 18:59:48   15,495 游览

测试集群有三个broker

broker0:9099
broker1:9091
broker2:9092

出现问题:

  1. 当单独杀掉9091或者单独杀掉9092,集群可以重新平衡,并很快恢复正常工作。

  2. 当单独杀掉9099时,消费者就无法消费了(生产者可以正常工作)

对consumer源码进行了跟踪,分析了下流程,发现问题阻塞在 consumer选取coordinator时

ensureCoordinatorReady()
        lookupCoordinator()

流程大概说就是

  1. consumer会从集群中选取一个broker作为coordinator,
  2. 然后group中的consumer会向coordinator发请求申请成为consumergroup中的leader
  3. 最后有1个consumer会成为consumerLeader ,其他consumer成为follower
  4. consumerLeader做分区分配任务,同步给coordinator
  5. consumerFollower从coordinator同步分区分配数据

问题出现在第1步

只有consumer将9099作为coordinator,并向9099broker发送申请时,会获得成功反馈

ClientResponse(receivedTimeMs=1515898295592, disconnected=false, request=ClientRequest(expectResponse=true, callback=org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient$RequestFutureCompletionHandler@7bc58891, request=RequestSend(header={api_key=10,api_version=0,correlation_id=30285,client_id=consumer-1}, body={group_id=testGroup}), createdTimeMs=1515898285668, sendTimeMs=1515898295578), **responseBody={error_code=0,coordinator={node_id=0,host=192.168.0.108,port=9099}})**

当consumer依次尝试将9091,9092作为coordinator并依次发送申请时,都会失败,并得到如下反馈

ClientResponse(receivedTimeMs=1515897561105, disconnected=false, request=ClientRequest(expectResponse=true, callback=org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient$RequestFutureCompletionHandler@488f3dd1, request=RequestSend(header={api_key=10,api_version=0,correlation_id=30281,client_id=consumer-1}, body={group_id=testGroup}), createdTimeMs=1515897558800, sendTimeMs=1515897561104), 
responseBody=**{error_code=15,coordinator={node_id=-1,host=,port=-1}})**

为啥会是这样的呢

responseBody=**{error_code=15,coordinator={node_id=-1,host=,port=-1}})**

9099这个broker与众不同之处在哪,拿着这个错误翻墙找了一波google第一条就找到了
https://stackoverflow.com/questions/42362911/kafka-high-level-consumer-error-code-15

As it turned out, the all partitions of the __consumer_offsets topic were located on dead nodes (nodes that I turned off and that will never come back). I solved the issue by shutting the cluster down, deleting the __consumer_offsets topic from Zookeeper and then starting the cluster again.

然后上zk看了下__consumer_offsets这个topic的分区副本情况,果然50个分区全在broker0上,我也是醉了

> bin/kafka-topics.sh --describe --zookeeper localhost:2182 --topic __consumer_offsets

Topic:__consumer_offsets    PartitionCount:50   ReplicationFactor:1 Configs:segment.bytes=104857600,cleanup.policy=compact,compression.type=producer
    Topic: __consumer_offsets   Partition: 0    Leader: 0   Replicas: 0 Isr: 0
    Topic: __consumer_offsets   Partition: 1    Leader: 0   Replicas: 0 Isr: 0
    Topic: __consumer_offsets   Partition: 2    Leader: 0   Replicas: 0 Isr: 0
    Topic: __consumer_offsets   Partition: 3    Leader: 0   
......

然后zk上删掉 __consumer_offsets目录, 重启集群每一个broker,此时zookeeper上__consumer_offsets还并没有生成,要开启消费者之后才会生成。

然后再观察__consumer_offsets,分区已经均匀分布在三个broker上面了

> bin/kafka-topics.sh --zookeeper localhost:2182 --describe --topic __consumer_offsets

Topic:__consumer_offsets    PartitionCount:50   ReplicationFactor:1 Configs:segment.bytes=104857600,cleanup.policy=compact,compression.type=producer
    Topic: __consumer_offsets   Partition: 0    Leader: 2   Replicas: 2 Isr: 2
    Topic: __consumer_offsets   Partition: 1    Leader: 0   Replicas: 0 Isr: 0
    Topic: __consumer_offsets   Partition: 2    Leader: 1   Replicas: 1 Isr: 1
    Topic: __consumer_offsets   Partition: 3    Leader: 2   Replicas: 2 Isr: 2
    .........

以为问题出在这里
就是在集群还未充分启动完全的情况下,客户端就开始了消费者进程进行消费,导致用于内部偏移量收集的 topic : __consumer_offsets 只分布到了已启动的broker,当这个broker挂掉,集群便不可用了,所以当__consumer_offsets的所有partition均匀分布到多个broker,按道理就应该可以实现高可用了

于是,再重试一次,问题还是出现了..........简直蛋都碎了

高可用依旧失败,发现整个集群能否正常运行依赖于首个启动的broker

无奈,回头又看了下配置文件,有关于 __consumer_offsets的配置

############################# Internal Topic Settings  #############################
# The replication factor for the group metadata internal topics "__consumer_offsets" and "__transaction_state"
# For anything other than development testing, a value greater than 1 is recommended for to ensure availability such as 3.
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1

确保高可用需要改成3,修改成3之后,终于实现了高可用,3个broker,只要存活任意1个broker就可以正常工作

所以总结

  1. 要保证__consumer_offsets的所有分区可用,offsets.topic.replication.factor至少配置为3
  2. 要保证集群所有的broker启动后,客户端再进行消费,否则会导致__consumer_offsets主题的分区副本不能均匀分布到每个broker,这样一旦某个副本所在broker全挂掉,就不能消费了?

未解之谜???

offsets.topic.replication.factor为1的时候,不管__consumer_offsets分区怎么分布,反正只要首个启动的broker存活,集群就能工作......

上个提问这里,不能修改所以重新开贴 https://www.orchome.com/792

发表于 2018-01-24
添加评论

__consumer_offsets是存储消费者组的offset的,所以它必须是高可用(副本数大于1)。

offsets.topic.replication.factor=1 的时候,不管__consumer_offsets分区怎么分布,反正只要首个启动的broker存活,集群就能工作......

这句话不够严谨,当你的kafka集群节点为3个的时候,搭建完成之后开始第一次测试的时候,__consumer_offsets才会创建,__consumer_offsets就会分布在这3台上,所以挂掉任何一台都会有影响部分消费者。出现这样的问题是因为当你刚刚搭建1台kafka节点的时候,就进行了测试,这个时候__consumer_offsets创建的时候,只有一个节点,所以会全部创建在首个启动的broker上,所以导致你们认为的首个broker不能挂。

“副本数”是保障kafka的topic分区高可用的关键,__consumer_offsets也是作为topic来维护的,默认情况下offsets.topic.replication.factor=1,即使kafka集群调整后,已经创建的topic也不会自动增加,所以必须你手动调整扩展副本数。

参考这里来调整副本数:kafka命令大全之迁移分区

offsets.topic.replication.factor为1的时候,不管__consumer_offsets分区怎么分布,反正只要首个启动的broker存活,集群就能工作......

这个是因为消费者消费offsets topics的副本如果只存在leader里存了一份,当刚好是这个leader的broker挂掉时,即使其他broker竞选成功为leader,他那里也没有消费者的offsets信息。

solo -> 原野 6年前
Topic:consumer_offsets    PartitionCount:50    ReplicationFactor:1    Configs:segment.bytes=104857600,cleanup.policy=compact,compression.type=producer
    Topic: __consumer_offsets    Partition: 0    Leader: 2    Replicas: 2    Isr: 2
    Topic: __consumer_offsets    Partition: 1    Leader: 0    Replicas: 0    Isr: 0
    Topic: __consumer_offsets    Partition: 2    Leader: 1    Replicas: 1    Isr: 1
    Topic: __consumer_offsets    Partition: 3    Leader: 2    Replicas: 2    Isr: 2

但是从这里可以看到,并没有所有partion都在一个broker上,三个broker都有offset的分区。所以你说 "当刚好是这个leader的broker挂掉时,即使其他broker竞选成功为leader,他那里也没有消费者的offsets信息" 似乎并不成立。

原野 -> solo 6年前

一个消费者组消费消息的offset只会存在__consumer_offsets里的一个分区上面,所以如果副本只有一份,那就只会在特定的broker上

solo -> 原野 6年前

我明白你的意思了,应该是这么回事

你好,我也出现了一样的问题

但是很奇怪的时,我把offsets.topic.replication.factor已经设置为23了,但是broker1挂掉时消费者还是无法消费,其他broker2或broker3挂掉没有关系;

另外奇怪的是,offsets.topic.replication.factor设置为3后,查看__consumer_offsets的每个分区的副本数,还是只有一个,我是否要删掉__consumer_offsets再重新创建呢?

solo -> 原野 6年前

要在zk里面把__consumer_offsets节点和下面的所有节点都删掉,再重新启动集群做消费。

原野 -> solo 6年前

问题已经解决,多谢

通过删除命令删除不了__consumer_offsets,应该是在server.properties文件里面配置命令。 我是通过关掉所有的服务 然后删除所有的logs,重启zk +kafka后实现删除。

另外我刚发现,删除后的节点A如果重新启动的话 通过命令(.\bin\windows\kafka-topics.bat --describe --zookeeper localhost:2181 --topic __consumer_offsets 我是windows跑的demo.)

查看__consumer_offsets的详情发现 Leader A并没有添加,所以我大胆猜测这里还是有一些问题的。

rheff -> rheff 4年前

忽略这个问题 刷新后出现了。

今天特意研究了一下这个问题,专门来登录回答一下楼主的疑问:

其实消费者无法消费有如下两种情况:

1)就是consumer_offsets副本数为1,当你所使用的消费者分组所对应的__consumer_offsets分区所在的broker挂掉了的时候,消费者就无法读取offset和提交offset了,这就是楼主所分析的这种现象,通过根据集群规模修改offsets.topic.replication.factor大于1即可解决。

2)就是楼主的“未解之谜”, 当你在未创建topic的情况下直接使用kafka-console-producer.sh创建生产者的时候,所创建的topic的默认分区数为1,副本数为1, 也就是说此时此topic的所有分区和副本都只存在于一个kafka broker上(一般都是第一个启动的broker,具体源码还没来得及看),此时,无论你所用的消费者所属的consumer_offsets分区再哪个broker上,只要broker1挂了,生产者和消费者都不可用,因为topic所在分区挂了,生产者肯定无法写数据了,那么消费者需要从topic分区fetch数据,肯定也不行了。
如果,topic分区副本和消费者组所属的consumer-offsets不在一个broker上的话,此时,如果topic分区所在的broker挂了,那么生产者无法使用,如果消费者组所属的__consumer-offsets分区所在的broker挂了,那么消费者不可用。

经过我的反复测试,在三节点的集群中,只要把所使用的topic副本数设置为大于1即可,offsets.topic.replication.factor大于1即可解决这种问题!

你的答案

查看kafka相关的其他问题或提一个您自己的问题