Spark+Kafka的Direct方式将偏移量发送到Zookeeper的实现
来源:互联网 发布:华讯网络好吗 编辑:程序博客网 时间:2024/06/08 16:00
Apache Spark 1.3.0引入了Direct API,利用Kafka的低层次API从Kafka集群中读取数据,并且在Spark Streaming系统里面维护偏移量相关的信息,并且通过这种方式去实现零数据丢失(zero data loss)相比使用基于Receiver的方法要高效。但是因为是Spark Streaming系统自己维护Kafka的读偏移量,而Spark Streaming系统并没有将这个消费的偏移量发送到Zookeeper中,这将导致那些基于偏移量的Kafka集群监控软件(比如:Apache Kafka监控之Kafka Web Console、Apache Kafka监控之KafkaOffsetMonitor等)失效。本文就是基于为了解决这个问题,使得我们编写的Spark Streaming程序能够在每次接收到数据之后自动地更新Zookeeper中Kafka的偏移量。
KafkaCluster类用于建立和Kafka集群的链接相关的操作工具类,我们可以对Kafka中Topic的每个分区设置其相应的偏移量Map((topicAndPartition, offsets.untilOffset)),然后调用KafkaCluster类的setConsumerOffsets方法去更新Zookeeper里面的信息,这样我们就可以更新Kafka的偏移量,最后我们就可以通过KafkaOffsetMonitor之类软件去监控Kafka中相应Topic的消费信息,下图是KafkaOffsetMonitor的监控情况:
我们从Spark的官方文档可以知道,维护Spark内部维护Kafka便宜了信息是存储在HasOffsetRanges类的offsetRanges中,我们可以在Spark Streaming程序里面获取这些信息:
val offsetsList = rdd.asInstanceOf[HasOffsetRanges].offsetRanges这样我们就可以获取所以分区消费信息,只需要遍历offsetsList,然后将这些信息发送到Zookeeper即可更新Kafka消费的偏移量。完整的代码片段如下:
val messages = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topicsSet) messages.foreachRDD(rdd => { val offsetsList = rdd.asInstanceOf[HasOffsetRanges].offsetRanges val kc = new KafkaCluster(kafkaParams) for (offsets < - offsetsList) { val topicAndPartition = TopicAndPartition("test-topic", offsets.partition) val o = kc.setConsumerOffsets(args(0), Map((topicAndPartition, offsets.untilOffset))) if (o.isLeft) { println(s"Error updating the offset to Kafka cluster: ${o.left.get}") } }})
KafkaCluster类用于建立和Kafka集群的链接相关的操作工具类,我们可以对Kafka中Topic的每个分区设置其相应的偏移量Map((topicAndPartition, offsets.untilOffset)),然后调用KafkaCluster类的setConsumerOffsets方法去更新Zookeeper里面的信息,这样我们就可以更新Kafka的偏移量,最后我们就可以通过KafkaOffsetMonitor之类软件去监控Kafka中相应Topic的消费信息,下图是KafkaOffsetMonitor的监控情况:
从图中我们可以看到KafkaOffsetMonitor监控软件已经可以监控到Kafka相关分区的消费情况,这对监控我们整个Spark Streaming程序来非常重要,因为我们可以任意时刻了解Spark读取速度。另外,KafkaCluster工具类的完整代码如下:
package org.apache.spark.streaming.kafkaimport kafka.api.OffsetCommitRequestimport kafka.common.{ErrorMapping, OffsetMetadataAndError, TopicAndPartition}import kafka.consumer.SimpleConsumerimport org.apache.spark.SparkExceptionimport org.apache.spark.streaming.kafka.KafkaCluster.SimpleConsumerConfigimport scala.collection.mutable.ArrayBufferimport scala.util.Randomimport scala.util.control.NonFatalclass KafkaCluster(val kafkaParams: Map[String, String]) extends Serializable { type Err = ArrayBuffer[Throwable] @transient private var _config: SimpleConsumerConfig = null def config: SimpleConsumerConfig = this.synchronized { if (_config == null) { _config = SimpleConsumerConfig(kafkaParams) } _config } def setConsumerOffsets(groupId: String, offsets: Map[TopicAndPartition, Long] ): Either[Err, Map[TopicAndPartition, Short]] = { setConsumerOffsetMetadata(groupId, offsets.map { kv => kv._1 -> OffsetMetadataAndError(kv._2) }) } def setConsumerOffsetMetadata(groupId: String, metadata: Map[TopicAndPartition, OffsetMetadataAndError] ): Either[Err, Map[TopicAndPartition, Short]] = { var result = Map[TopicAndPartition, Short]() val req = OffsetCommitRequest(groupId, metadata) val errs = new Err val topicAndPartitions = metadata.keySet withBrokers(Random.shuffle(config.seedBrokers), errs) { consumer => val resp = consumer.commitOffsets(req) val respMap = resp.requestInfo val needed = topicAndPartitions.diff(result.keySet) needed.foreach { tp: TopicAndPartition => respMap.get(tp).foreach { err: Short => if (err == ErrorMapping.NoError) { result += tp -> err } else { errs.append(ErrorMapping.exceptionFor(err)) } } } if (result.keys.size == topicAndPartitions.size) { return Right(result) } } val missing = topicAndPartitions.diff(result.keySet) errs.append(new SparkException(s"Couldn't set offsets for ${missing}")) Left(errs) } private def withBrokers(brokers: Iterable[(String, Int)], errs: Err) (fn: SimpleConsumer => Any): Unit = { brokers.foreach { hp => var consumer: SimpleConsumer = null try { consumer = connect(hp._1, hp._2) fn(consumer) } catch { case NonFatal(e) => errs.append(e) } finally { if (consumer != null) { consumer.close() } } } } def connect(host: String, port: Int): SimpleConsumer = new SimpleConsumer(host, port, config.socketTimeoutMs, config.socketReceiveBufferBytes, config.clientId)}
0 0
- Spark+Kafka的Direct方式将偏移量发送到Zookeeper的实现
- Spark+Kafka的Direct方式将偏移量发送到Zookeeper的实现
- Spark+Kafka的Direct方式将偏移量发送到Zookeeper实现
- Spark+Kafka的Direct方式将偏移量发送到Zookeeper实现
- Spark+Kafka的Direct方式将偏移量发送到Zookeeper实现
- Spark+Kafka的Direct方式将偏移量发送到Zookeeper实现
- spark+kafka的Direct方式更新消费便宜量到zookeeper
- 将 Spark Streaming + Kafka direct 的 offset 保存进入Zookeeper
- 将 Spark Streaming + Kafka direct 的 offset 保存进入Zookeeper(二)
- zookeeper上修改kafka消费组的偏移量
- Spark Streaming之Kafka的Receiver和Direct方式
- Kafka 消息偏移量的维护
- 如何管理Spark Streaming消费Kafka的偏移量(一)
- 如何管理Spark Streaming消费Kafka的偏移量(二)
- 如何管理Spark Streaming消费Kafka的偏移量(三)
- kafka同步zookeeper前移偏移量
- Spark Streaming基于kafka的Direct详解
- Spark Streaming基于kafka的Direct详解
- HDU 5531 几何公式
- Firefox 与 sublime text 3 通过LiveReload插件实现前端代码实时预览
- flask 实现上传图片并缩放作为头像
- POJ 1930 Dead Fraction(小数化分数)
- 【USACO5.3.3】Network of Schools
- Spark+Kafka的Direct方式将偏移量发送到Zookeeper的实现
- python标准库学习3-fileinput
- B树、B-树、B+树、B*树 红黑树
- OpenLayers 3 之 事件体系详解
- 页面生成树形查询的后台实现方法
- zjnu MAFIJA (图论)
- POJ 2098 数值积分
- 257. Binary Tree Paths
- 7. Java 注释类型