pykafka的SessionExpiredError、PartitionOwnedError、ConsumerStoppedException

这两天一直在使用pykafka做简单的开发,但是这个看起来简单的开发任务中却遇到了几个问题,在网上找了一下好像关于这几个问题的讨论(回答)不多,在这里记录一下,为其他遇到类似问题的同学提供一些思路。

任务简化一下就是多个输入把日志信息输入kafka队列中,为了提高处理的效率在队列的另外一端开启多个进程处理日志信息,也就是对一个topic有多个consumers,所以理所当然的用到了kafka的balancedconsumer

关于Kafka的Python包有kafka-python、pykafka,网上说kafka-python对kafka的balancedconsumer支持不太好所以我选择了pykafka。pykafka的文档对于新手入门介绍的很少,就两个简单的示例,看了一下入门教程然后大致浏览了一遍常用的函数的文档就开始上手写代码了。本身我对Kafka的了解也有限也就是这几天突击了解了一下,没有详细的看过Kafka的完整document,这些也就为后面的问题埋下了隐患。
1. SessionExpiredError
consumer.consume()得到一条日志以后我需要做一些正则表达式的处理,在自测的过程中偶尔会出现SessionExpiredError的错误,一看这个异常的名称大概就知道这是因为客户端和zookeeper之间的session过期导致的。
从一些文档中看到consumer会周期的发送heartbeat给zookeeper以保持链接,那么我理解无论我处理消息的时间多长都应该不会导致session过期的,我浏览了一遍pykafka的代码好像就没有看到consumer周期发送heartbeat的这部分代码,这块可能还需要后续再确认一下。
上面提到session过期的可能原因就是我处理消息的时间太长导致的,所以考虑只要增加一下session的过期时间应该就能解决,而且从Kafka的文档中我也找到了session.timeout.ms这个参数,但是在pykafka的文档中死活没有找到设置这个参数的地方。没办法只能把BalancedConsumer每一个参数都仔细琢磨一遍,做了一遍排除法,最后发现是zookeeper_connection_timeout_ms这个参数来控制的,娘亲啊!
zookeeper_connection_timeout_ms设置成60000就解决了这个问题。
2. PartitionOwnedError、ConsumerStoppedException
使用BalancedConsumer那就是为了提高处理效率,所以我新建了一个有10个partitions的topic,开启了10个处理进程。但是在测试的过程中出现了PartitionOwnedError、ConsumerStoppedException这两个错误,而且是不一定完全重现。
在网上搜了一下找到的关于这两个错误的内容也非常有限,我觉得主要还是pykafka使用的人不太多导致的,没办法也只能自己来琢磨解决了。
首先在pykafka的exception.py中找到了这两个exception的定义,PartitionOwnedError的定义大致是说这个partition在zookeeper中已经被其它的worker占有了,你不能再次请求使用它。ConsumerStoppedException的意思是说该consumer已经停止了,你不能再对它有操作了。
好了,知道了定义那么就从代码里面找一下看看具体哪些地方会抛出这些异常,PartitionOwnedError在balancedconsumer.py的_add_partitions()、_update_member_assignment()这两个函数中会有可能抛出,我们先看一下这两个地方的函数调用栈:

BalancedConsumer class->start function->_rebalance function->_update_member_assignment function->_add_partitions function

关于这些函数调用过程强烈建议大家结合理论来看,可以参考:Kafka设计解析(四)- Kafka Consumer设计解析,在High Level Consumer Rebalance章节讲到了Kafka的Rebalance过程,也就是我们前面提到的函数_rebalance()
我的topic有10个partitions,那么当第一个进程启动的时候这个consumer是拥有(owned)这10个partition的,那么当我启动第二个进程的时候根据Kafka的rebalance规则第一个consumer根据注册在zookeeper上面的watch看到了有更多的consumer加入了那么就需要把它拥有的partition分一些出来给新增的consumer,当然这个是根据rebalance算法来进行的。看了pykafka源码的实现知道第二个consumer并不是等到第一个consumer”让”出来partition以后再请求partition,也就是说一个group中的consumer是同时进行rebalance的。这样就会导致同一个group中的consumer在请求partition的时候出现PartitionOwnedError错误。
有两个参数rebalance_max_retries、rebalance_backoff_ms是控制rebalance的过程的,rebalance_max_retries是控制rebalance的重试次数,rebalance_backoff_ms是两次重试之间的sleep时间。如果这两个参数的值都比较小的话那么就会存在第一个consumer还没”让”partition第二个consumer就已经退出了rebalance过程,这样就会导致consumer获取partition失败而抛出ConsumerStoppedException异常。
PartitionOwnedError错误可以忽略,但是也需要注意,因为有可能会导致ConsumerStoppedException异常。那么怎么来解决这个异常呢,那就是适当的调大rebalance_max_retries、rebalance_backoff_ms参数值。
还有一种情况就是如果我们非常暴力的用kill SIGKILL来停掉consumer那么就会导致这一次在zookeeper上面的信息没有来得及被删除,那么当下一次同样的consumer group启动的时候因为zookeeper上面的partition的consumer_id跟本次的consumer_id不一致导致partition既不能被删除也不能被拥有,也会出现PartitionOwnedError、ConsumerStoppedException

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页