原文链接:Spark+Kafka的Direct方式将偏移量发送到Zookeeper实现

 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 ConsoleApache Kafka监控之KafkaOffsetMonitor等)失效。本文就是基于为了解决这个问题,使得我们编写的Spark Streaming程序能够在每次接收到数据之后自动地更新Zookeeper中Kafka的偏移量。

  我们从Spark的官方文档可以知道,维护Spark内部维护Kafka便宜了信息是存储在HasOffsetRanges类的offsetRanges中,我们可以在Spark Streaming程序里面获取这些信息:

1 val offsetsList = rdd.asInstanceOf[HasOffsetRanges].offsetRanges

这样我们就可以获取所以分区消费信息,只需要遍历offsetsList,然后将这些信息发送到Zookeeper即可更新Kafka消费的偏移量。完整的代码片段如下:

01 val messages =KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topicsSet)
02       messages.foreachRDD(rdd => {
03         val offsetsList = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
04         val kc = new KafkaCluster(kafkaParams)
05         for (offsets < - offsetsList) {
06           val topicAndPartition = TopicAndPartition("iteblog", offsets.partition)
07           val =kc.setConsumerOffsets(args(0), Map((topicAndPartition, offsets.untilOffset)))
08           if (o.isLeft) {
09             println(s"Error updating the offset to Kafka cluster: ${o.left.get}")
10           }
11         }
12 })

  KafkaCluster类用于建立和Kafka集群的链接相关的操作工具类,我们可以对Kafka中Topic的每个分区设置其相应的偏移量Map((topicAndPartition, offsets.untilOffset)),然后调用KafkaCluster类的setConsumerOffsets方法去更新Zookeeper里面的信息,这样我们就可以更新Kafka的偏移量,最后我们就可以通过KafkaOffsetMonitor之类软件去监控Kafka中相应Topic的消费信息,下图是KafkaOffsetMonitor的监控情况:

  从图中我们可以看到KafkaOffsetMonitor监控软件已经可以监控到Kafka相关分区的消费情况,这对监控我们整个Spark Streaming程序来非常重要,因为我们可以任意时刻了解Spark读取速度。另外,KafkaCluster工具类的完整代码如下:

01 package org.apache.spark.streaming.kafka
02  
03 import kafka.api.OffsetCommitRequest
04 import kafka.common.{ErrorMapping, OffsetMetadataAndError, TopicAndPartition}
05 import kafka.consumer.SimpleConsumer
06 import org.apache.spark.SparkException
07 import org.apache.spark.streaming.kafka.KafkaCluster.SimpleConsumerConfig
08  
09 import scala.collection.mutable.ArrayBuffer
10 import scala.util.Random
11 import scala.util.control.NonFatal
12  
13 /**
14  * User: 过往记忆
15  * Date: 2015-06-02
16  * Time: 下午23:46
17  * bolg: http://www.iteblog.com
18  * 本文地址:http://www.iteblog.com/archives/1381
19  * 过往记忆博客,专注于hadoop、hive、spark、shark、flume的技术博客,大量的干货
20  * 过往记忆博客微信公共帐号:iteblog_hadoop
21  */
22  
23 class KafkaCluster(val kafkaParams: Map[String, String]) extends Serializable {
24   type Err = ArrayBuffer[Throwable]
25  
26   @transient private var _config: SimpleConsumerConfig = null
27  
28   def config: SimpleConsumerConfig = this.synchronized {
29     if (_config == null) {
30       _config = SimpleConsumerConfig(kafkaParams)
31     }
32     _config
33   }
34  
35   def setConsumerOffsets(groupId: String,
36                          offsets: Map[TopicAndPartition, Long]
37                           ): Either[Err, Map[TopicAndPartition, Short]] = {
38     setConsumerOffsetMetadata(groupId, offsets.map { kv =>
39       kv._1 -> OffsetMetadataAndError(kv._2)
40     })
41   }
42  
43   def setConsumerOffsetMetadata(groupId: String,
44                                 metadata: Map[TopicAndPartition, OffsetMetadataAndError]
45                                  ): Either[Err, Map[TopicAndPartition, Short]] = {
46     var result = Map[TopicAndPartition, Short]()
47     val req = OffsetCommitRequest(groupId, metadata)
48     val errs = new Err
49     val topicAndPartitions = metadata.keySet
50     withBrokers(Random.shuffle(config.seedBrokers), errs) { consumer =>
51       val resp = consumer.commitOffsets(req)
52       val respMap = resp.requestInfo
53       val needed = topicAndPartitions.diff(result.keySet)
54       needed.foreach { tp: TopicAndPartition =>
55         respMap.get(tp).foreach { err: Short =>
56           if (err == ErrorMapping.NoError) {
57             result += tp -> err
58           else {
59             errs.append(ErrorMapping.exceptionFor(err))
60           }
61         }
62       }
63       if (result.keys.size == topicAndPartitions.size) {
64         return Right(result)
65       }
66     }
67     val missing = topicAndPartitions.diff(result.keySet)
68     errs.append(new SparkException(s"Couldn't set offsets for ${missing}"))
69     Left(errs)
70   }
71  
72   private def withBrokers(brokers: Iterable[(String, Int)], errs: Err)
73                          (fn: SimpleConsumer => Any): Unit = {
74     brokers.foreach { hp =>
75       var consumer: SimpleConsumer = null
76       try {
77         consumer = connect(hp._1, hp._2)
78         fn(consumer)
79       catch {
80         case NonFatal(e) =>
81           errs.append(e)
82       finally {
83         if (consumer != null) {
84           consumer.close()
85         }
86       }
87     }
88   }
89  
90   def connect(host: String, port: Int): SimpleConsumer =
91     new SimpleConsumer(host, port, config.socketTimeoutMs,
92       config.socketReceiveBufferBytes, config.clientId)
93 }

Spark+Kafka的Direct方式将偏移量发送到Zookeeper实现(转)的更多相关文章

  1. sparkStreaming消费kafka-0.8方式:direct方式(存储offset到zookeeper)

    生产中,为了保证kafka的offset的安全性,并且防止丢失数据现象,会手动维护偏移量(offset) 版本:kafka:0.8 其中需要注意的点: 1:获取zookeeper记录的分区偏移量 2: ...

  2. sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)-- 2

    参考上篇博文:https://www.cnblogs.com/niutao/p/10547718.html 同样的逻辑,不同的封装 package offsetInZookeeper /** * Cr ...

  3. sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)

    版本声明: kafka:1.0.1 spark:2.1.0 注意:在使用过程中可能会出现servlet版本不兼容的问题,因此在导入maven的pom文件的时候,需要做适当的排除操作 <?xml ...

  4. SparkStreaming和Kafka基于Direct Approach如何管理offset实现exactly once

    在之前的文章<解析SparkStreaming和Kafka集成的两种方式>中已详细介绍SparkStreaming和Kafka集成主要有Receiver based Approach和Di ...

  5. Spark Streaming消费Kafka Direct方式数据零丢失实现

    使用场景 Spark Streaming实时消费kafka数据的时候,程序停止或者Kafka节点挂掉会导致数据丢失,Spark Streaming也没有设置CheckPoint(据说比较鸡肋,虽然可以 ...

  6. kafka结合Spark-streming的直连(Direct)方式

     说明:此程序使用的scala编写 在spark-stream+kafka使用的时候,有两种连接方式一种是Receiver连接方式,一种是Direct连接方式. 两种连接方式简介: Receiver接 ...

  7. 大数据学习day33----spark13-----1.两种方式管理偏移量并将偏移量写入redis 2. MySQL事务的测试 3.利用MySQL事务实现数据统计的ExactlyOnce(sql语句中出现相同key时如何进行累加(此处时出现相同的单词))4 将数据写入kafka

    1.两种方式管理偏移量并将偏移量写入redis (1)第一种:rdd的形式 一般是使用这种直连的方式,但其缺点是没法调用一些更加高级的api,如窗口操作.如果想更加精确的控制偏移量,就使用这种方式 代 ...

  8. 大数据Spark+Kafka实时数据分析案例

    本案例利用Spark+Kafka实时分析男女生每秒购物人数,利用Spark Streaming实时处理用户购物日志,然后利用websocket将数据实时推送给浏览器,最后浏览器将接收到的数据实时展现, ...

  9. 【python】spark+kafka使用

    网上用python写spark+kafka的资料好少啊 自己记录一点踩到的坑~ spark+kafka介绍的官方网址:http://spark.apache.org/docs/latest/strea ...

随机推荐

  1. Ninject

    一.为什么要使用依赖注入框架 依赖注入框架也叫IoC容器.它的作用使类与类之间解耦 我们看看为什么要用依赖注入框架,举个几个梨子: 1,高度耦合的类 有一个Order类,Order类是用于订单操作的, ...

  2. c#程序员机试题

    一.题目: 有一数组: int[] arr = new int[] { 48,1,3,55,15,29,12,33,26,41,56,32}; 1.求出最大值 2.按每个数字的10位数分组(说明:0~ ...

  3. [leetcode sort]148. Sort List

    Sort a linked list in O(n log n) time using constant space complexity. 以时间复杂度O(n log n)排序一个链表. 归并排序, ...

  4. MyBatis 插入时返回刚插入记录的主键值

    MyBatis 插入时返回刚插入记录的主键值 一.要求: 1.数据库表中的主键是自增长的,如:id: 2.获取刚刚插入的记录的id值: 二.源代码: 1.User.java package cn.co ...

  5. 关于mysql_connect CLIENT_MULTI_RESULTS

    自己写了一个mysql存储过程,以为php有用于调用存储过程的内建函数,查了一下发现只能用mysql_query(call pro())这样的方式,我认为从本质上也就相当于在mysql命令行里执行语句 ...

  6. poj 1509

    求一个字符串在旋转置换群下最小字典表示. 用的是后缀数组(后缀自动机还是再听听jason_yu讲讲吧,关于right集合的部分还有问题) 最小表示法的思想很有好(判断两个对象在某一置换群划分下,是否等 ...

  7. 【51nod-1239&1244】欧拉函数之和&莫比乌斯函数之和 杜教筛

    题目链接: 1239:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1239 1244:http://www.51nod. ...

  8. Ubuntu16.04配置Android5.0编译环境

    1.安装jdk7 Ubuntu16.04的安装源已经默认没有openjdk7了,所以要自己手动添加仓库,如下: $ sudo add-apt-repository ppa:openjdk-r/ppa ...

  9. Python3使用setuptools

    旧版的setuptools已经不能用于Python3上了,国外有大神fork出了另一分支distribute,它可以支持Python3:     1 2 3 4 5 6 7 #下载 wget http ...

  10. 正确率、召回率和F值

    正确率.召回率和F值是在鱼龙混杂的环境中,选出目标的重要评价指标. 不妨看看这些指标的定义先: 正确率 = 正确识别的个体总数 /  识别出的个体总数 召回率 = 正确识别的个体总数 /  测试集中存 ...