笔者在使用SparkStreaming读取kafka进行实时计算时,在数据量大了以后总是会报Failed to get records for … after polling for …的问题,具体就是在规定的时间内没有从Kafka拉取到数据,从前到后查了不少资料,直至问题解决,现将过程记录如下
Spark配置层面
首先,针对这个问题,最直观的两个参数是spark.streaming.kafka.consumer.poll.ms和spark.task.maxFailures,分别表示拉取Kafka最大等待时间和失败重试次数,对于多数应用来说,一般失败进行一次重试就能拉倒数据了,浪费一些时间也无伤大雅,在出事阶段我们也是一直用这种方式让流计算正常运行,对于没有设置这俩参数的同学可以设置试试,其中spark.streaming.kafka.consumer.poll.ms建议稍微设置大一点,防止Kafka Broker GC等因素确实存在的短时间拉不到数据的可能,我们设置的是5000ms。
但是到了后来,我们发现随着数据量的增大,失败的次数越来越多,且拉数据这里浪费时间严重,必须寻求解决方案彻底解决这个问题了。
Kafka配置层面
最开始,从网上搜到解决方案是设置参数reconnect.backoff.ms参数为0,但是笔者在自己的应用程序中设置后发现问题依然存在。
后续,笔者从Spark的issue列表中找到的一个解决方案,具体可参考 https://issues.apache.org/jira/browse/SPARK-19275 ,大致意思是说了request.timeout.ms ,session.timeout.ms,heartbeat.interval.ms三个参数设置的问题,建议是设置这3个参数的值大于SparkStreaming的batchTime时间,以避免计算处理时间太长影响Kafka心跳汇报不及时触发超时,笔者试用了这种方案貌似可以减轻,但是在batchTime比较大情况下,不得已把心跳频率设置太大也会显得不合理,只能临时缓解,并不能彻底解决问题。
此外,还有一个Kafka参数max.poll.records也要合理配置,否则也会引起拉不到数据,建议设300~500之间。
KafkaClient本身层面
想到官方issue中提到的这个问题由Kafka心跳问题引起,那我们想为什么不能从心跳机制本身来解决这个问题呢,而不是规避问题。接着去研究了SparkStreaming读取Kafka的源码,我们用的是spark-streaming-kafka-0-10_2.11这个jar包,这个包中使用的KafkaClient是0.10.0.1版本的,这个版本的KafkaClient心跳汇报依赖后面的数据处理过程,而0.10.2.0之后的版本就已经把心跳汇报放到一个独立的线程中去了,所以只需要换掉Jar包中的KafkaClient然后升级一下即可,pom如下
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>2.2.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.10.2.1</version>
</dependency>
至此,Failed to get records for … after polling for …的问题便不会再报了