Spark+Kafka的Direct方式将偏移量发送到Zookeeper实现(转)
原文链接: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 Console、Apache 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 o =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实现(转)的更多相关文章
- sparkStreaming消费kafka-0.8方式:direct方式(存储offset到zookeeper)
生产中,为了保证kafka的offset的安全性,并且防止丢失数据现象,会手动维护偏移量(offset) 版本:kafka:0.8 其中需要注意的点: 1:获取zookeeper记录的分区偏移量 2: ...
- sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)-- 2
参考上篇博文:https://www.cnblogs.com/niutao/p/10547718.html 同样的逻辑,不同的封装 package offsetInZookeeper /** * Cr ...
- sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)
版本声明: kafka:1.0.1 spark:2.1.0 注意:在使用过程中可能会出现servlet版本不兼容的问题,因此在导入maven的pom文件的时候,需要做适当的排除操作 <?xml ...
- SparkStreaming和Kafka基于Direct Approach如何管理offset实现exactly once
在之前的文章<解析SparkStreaming和Kafka集成的两种方式>中已详细介绍SparkStreaming和Kafka集成主要有Receiver based Approach和Di ...
- Spark Streaming消费Kafka Direct方式数据零丢失实现
使用场景 Spark Streaming实时消费kafka数据的时候,程序停止或者Kafka节点挂掉会导致数据丢失,Spark Streaming也没有设置CheckPoint(据说比较鸡肋,虽然可以 ...
- kafka结合Spark-streming的直连(Direct)方式
说明:此程序使用的scala编写 在spark-stream+kafka使用的时候,有两种连接方式一种是Receiver连接方式,一种是Direct连接方式. 两种连接方式简介: Receiver接 ...
- 大数据学习day33----spark13-----1.两种方式管理偏移量并将偏移量写入redis 2. MySQL事务的测试 3.利用MySQL事务实现数据统计的ExactlyOnce(sql语句中出现相同key时如何进行累加(此处时出现相同的单词))4 将数据写入kafka
1.两种方式管理偏移量并将偏移量写入redis (1)第一种:rdd的形式 一般是使用这种直连的方式,但其缺点是没法调用一些更加高级的api,如窗口操作.如果想更加精确的控制偏移量,就使用这种方式 代 ...
- 大数据Spark+Kafka实时数据分析案例
本案例利用Spark+Kafka实时分析男女生每秒购物人数,利用Spark Streaming实时处理用户购物日志,然后利用websocket将数据实时推送给浏览器,最后浏览器将接收到的数据实时展现, ...
- 【python】spark+kafka使用
网上用python写spark+kafka的资料好少啊 自己记录一点踩到的坑~ spark+kafka介绍的官方网址:http://spark.apache.org/docs/latest/strea ...
随机推荐
- Sting.format字符串格式化
控制格式scanf printf 也不知道为什么=-= 越研究深层的java就越感觉它是从别的语言那抄袭来的
- NetCore+Dapper WebApi架构搭建(一):基本框架
初衷是想用dapper搭建一个高性能的架构,因为dapper操作数据库的效率很高 1.VS创建一个NetCore WebApi的框架,然后解决方案添加一个NetStandard的类库 整个解决方案如图 ...
- git merge和git rebase的区别(转)
Description git rebase 和 git merge 一样都是用于从一个分支获取并且合并到当前分支,但是他们采取不同的工作方式,以下面的一个工作场景说明其区别 场景: 如图所示: ...
- Java日常错误及需要注意细节,持续更新......
记录日常工作中一些容易被忽视的错误及细节,持续更新...... 一.问题:HashMap<Long, String>中,用get(Integer key)取不到值 Map<Long, ...
- 拆分Cocos2dx渲染部分代码
纹理实现 思想 这个是Cocos2dx的渲染部分的最基本的实现,被我拆分到mac上,但是并不是用的EGLContext,而是搭配glfw,还有soil第三方图形库. 实现 // // main.cpp ...
- 改变手机浏览器(iPhone/Android)上文本输入框的默认弹出键盘
iPhone/iPad和Android提供不同的的键盘输入类型,触发合适的键盘将极大地改善用户体验. 键盘类型 默认: 默认键盘的字母模式 数字: 默认键盘的数字模式,(含小数点等) 邮件: 与默 ...
- python开发_tkinter_菜单选项中英文切换_菜单选项不可用操作_博主推荐
我使用的python版本为:3.3.2 如果你对python中tkinter模块的菜单操作不是很了解,你可以看看: python开发_tkinter_窗口控件_自己制作的Python IDEL_博主推 ...
- ROS知识(9)----安装Turtlebot2和远程控制Turtlebot2
安装turtlebot2,场景为:turtlebot2上搭载着一台电脑主机A,该电脑作为主机Master,有自带的电源和3D传感器,roscore在该台机器上启动.pc电脑远程连接A,和A通讯,pc不 ...
- Error creating bean with name 'adminUserController': Injection of autowired dependencies failed;
spring 个坑爹地,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ...
- MVC文件上传02-使用HttpPostedFileBase上传多个文件
本篇体验上传多个文件.兄弟篇为: MVC文件上传01-使用jquery异步上传并客户端验证类型和大小 MVC最基本上传文件方式中的一个遗漏点 □ 前台视图部分 1: <% u ...