SparkStreaming的Receiver方式和直连方式有什么区别?

Receiver接收固定时间间隔的数据(放在内存中的),使用高级API,自动维护偏移量,达到固定的时间才去进行处理,效率低并且容易丢失数据,灵活性特别差,不好,而且它处理数据的时候,如果某一刻的数据量过大,那么就会造成磁盘溢写的情况,他通过WALS进行磁盘写入。

Receiver实现方式:

代码如下:

object KafkaWC02 {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("kafkaWC").setMaster("local[2]") //设置线程数
val ssc = new StreamingContext(conf, Seconds(5)) //设置检查点
ssc.checkpoint("D:\\data\\checpoint\\checpoint1")
//接下来编写kafka的配置信息
val zks = "spark01:2181"
//然后是kafka的消费组
val groupId = "gp1"
//Topic的名字 Map的key是Topic名字,第二个参数是线程数
val topics = Map[String, Int]("test02" -> 1)
//创建kafka的输入数据流,来获取kafka中的数据
val data = KafkaUtils.createStream(ssc, zks, groupId, topics)
//获取到的数据是键值对的格式(key,value)
//获取到的数据是 key是偏移量 value是数据
//接下来开始处理数据 val lines = data.flatMap(_._2.split(" "))
val words = lines.map((_, 1))
val res = words.updateStateByKey(updateFunc,new HashPartitioner(ssc.sparkContext.defaultParallelism),true)
res.print()
//val result = words.reduceByKey(_ + _)
//val res = result.updateStateByKey[Int](updateFunc)
//res.print()
//打印输出
//result.print()
//启动程序
ssc.start()
//等待停止
ssc.awaitTermination() }
//(iterator:Iteratot[(K,Seq[V]),Option[S]]))
//传过来的值是Key Value类型
//第一个参数,是我们从kafka获取到的元素,key ,String类型
//第二个参数,是我们进行单词统计的value值,Int类型
//第三个参数,是我们每次批次提交的中间结果集
val updateFunc=(iter:Iterator[(String,Seq[Int],Option[Int])])=>{
iter.map(t=>{
(t._1,t._2.sum+t._3.getOrElse(0))
})
}
}

  

Direct直连方式,

它使用的是底层API实现Offest我们开发人员管理,这样的话,它的灵活性特别好。并且可以保证数据的安全性,而且不用担心数据量过大,因为它有预处理机制,进行提前处理,然后再批次提交任务。

Direct实现方式:

代码如下:

import kafka.common.TopicAndPartition
import kafka.message.MessageAndMetadata
import kafka.serializer.StringDecoder
import kafka.utils.{ZKGroupTopicDirs, ZkUtils}
import org.I0Itec.zkclient.ZkClient
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka.{HasOffsetRanges, KafkaUtils, OffsetRange}
import org.apache.spark.streaming.{Duration, StreamingContext} /**
* 重要!!! Direct直连方式
*/
object KafkaDirectWC {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("Direct").setMaster("local[2]")
val ssc = new StreamingContext(conf,Duration(5000))
//指定组名
val groupId = "gp01"
//指定消费的topic名字
val topic = "tt"
//指定kafka的Broker地址(SparkStreaming的Task直接连接到Kafka分区上,用的是底层API消费)
val brokerList ="spark:9092"
//接下来我们要自己维护offset了,将offset保存到ZK中
val zkQuorum = "spark:2181"
//创建stream时使用的topic名字集合,SparkStreaming可以同时消费多个topic
val topics:Set[String] = Set(topic)
//创建一个ZkGroupTopicDirs对象,其实是指定往Zk中写入数据的目录
// 用于保存偏移量
val TopicDirs = new ZKGroupTopicDirs(groupId,topic)
//获取zookeeper中的路径“/gp01/offset/tt/”
val zkTopicPath = s"${TopicDirs.consumerOffsetDir}"
//准备kafka参数
val kafkas = Map(
"metadata.broker.list"->brokerList,
"group.id"->groupId,
//从头开始读取数据
"auto.offset.reset"->kafka.api.OffsetRequest.SmallestTimeString
)
// zookeeper 的host和ip,创建一个client,用于更新偏移量
// 是zookeeper客户端,可以从zk中读取偏移量数据,并更新偏移量
val zkClient = new ZkClient(zkQuorum)
//"/gp01/offset/tt/0/10001"
//"/gp01/offset/tt/1/20001"
//"/gp01/offset/tt/2/30001"
val clientOffset = zkClient.countChildren(zkTopicPath)
// 创建KafkaStream
var kafkaStream :InputDStream[(String,String)]= null
//如果zookeeper中有保存offset 我们会利用这个offset作为KafkaStream的起始位置
//TopicAndPartition [/gp01/offset/tt/0/ , 8888]
var fromOffsets:Map[TopicAndPartition,Long] = Map()
//如果保存过offset
if(clientOffset > 0){
//clientOffset 的数量其实就是 /gp01/offset/tt的分区数目
for(i<-0 until clientOffset){
// /gp01/offset/tt/ 0/10001
val partitionOffset = zkClient.readData[String](s"$zkTopicPath/${i}")
// tt/0
val tp = TopicAndPartition(topic,i)
//将不同partition 对应得offset增加到fromoffset中
// tt/0 -> 10001
fromOffsets += (tp->partitionOffset.toLong)
}
// key 是kafka的key value 就是kafka数据
// 这个会将kafka的消息进行transform 最终kafka的数据都会变成(kafka的key,message)这样的Tuple
val messageHandler = (mmd:MessageAndMetadata[String,String])=>
(mmd.key(),mmd.message())
// 通过kafkaUtils创建直连的DStream
//[String,String,StringDecoder, StringDecoder,(String,String)]
// key value key解码方式 value的解码方式 接收数据的格式
kafkaStream = KafkaUtils.createDirectStream
[String,String,StringDecoder,
StringDecoder,(String,String)](ssc,kafkas,fromOffsets,messageHandler)
}else{
//如果未保存,根据kafkas的配置使用最新的或者最旧的offset
kafkaStream = KafkaUtils.createDirectStream
[String,String,StringDecoder,StringDecoder](ssc,kafkas,topics)
}
//偏移量范围
var offsetRanges = Array[OffsetRange]()
//从kafka读取的数据,是批次提交的,那么这块注意下,
// 我们每次进行读取数据后,需要更新维护一下偏移量
//那么我们开始进行取值
// val transform = kafkaStream.transform{
// rdd=>
// //得到该RDD对应得kafka消息的offset
// // 然后获取偏移量
// offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
// rdd
// }
// val mes = transform.map(_._2)
// 依次迭代DStream中的RDD
kafkaStream.foreachRDD{
//对RDD进行操作 触发Action
kafkardd=> offsetRanges = kafkardd.asInstanceOf[HasOffsetRanges].offsetRanges //下面 你就可以怎么写都行了,为所欲为
val maps = kafkardd.map(_._2) maps.foreach(println) for(o<-offsetRanges){
// /gp01/offset/tt/ 0
val zkpath = s"${TopicDirs.consumerOffsetDir}/${o.partition}"
//将该partition的offset保存到zookeeper中
// /gp01/offset/tt/ 0/88889
ZkUtils.updatePersistentPath(zkClient,zkpath,o.untilOffset.toString)
}
}
// 启动
ssc.start()
ssc.awaitTermination()
}
}

  

SparkSteaming中直连与receiver两种方式的区别的更多相关文章

  1. Redis持久化的两种方式和区别

    该文转载自:http://www.cnblogs.com/swyi/p/6093763.html Redis持久化的两种方式和区别 Redis是一种高级key-value数据库.它跟memcached ...

  2. 引入外部CSS的两种方式及区别

    1.CSS的两种引入方式 通过@import指令引入 @import指令是CSS语言的一部分,使用时把这个指令添加到HTML的一个<style>标签中: 要与外部的CSS文件关联起来,得使 ...

  3. Javascript绑定事件的两种方式的区别

    命名函数 <input type="button" onclick="check()" id="btn"/> <scrip ...

  4. JQuery中阻止事件冒泡的两种方式及其区别

    JQuery 提供了两种方式来阻止事件冒泡. 方式一:event.stopPropagation(); $("#div1").mousedown(function(event){ ...

  5. vue 路由传参 params 与 query两种方式的区别

    初学vue的时候,不知道如何在方法中跳转界面并传参,百度过后,了解到两种方式,params 与 query.然后,错误就这么来了:  router文件下index.js里面,是这么定义路由的: { p ...

  6. UIImage创建图片的两种方式的区别

    在工作中经常会遇到添加图片,用哪种方式添加更好呢?请看详解 方法一: UIImage *image = [UIImage imageNamed:@"haha"]; 这种方法创建的图 ...

  7. c++构造函数成员初始化中赋值和初始化列表两种方式的区别

    先总结下: 由于类成员初始化总在构造函数执行之前 1)从必要性: a. 成员是类或结构,且构造函数带参数:成员初始化时无法调用缺省(无参)构造函数 b. 成员是常量或引用:成员无法赋值,只能被初始化 ...

  8. js对象中属性调用.和[] 两种方式的区别

    JS 调用属性一般有两种方法——点和中括号的方法. 标准格式是对象.属性(不带双引号),注意一点的是:js对象的属性,key标准是不用加引号的,加也可以,特别的情况必须加,如果key数字啊,表达式啊等 ...

  9. svn检出两种方式的区别

    第一种是“做为新项目检出,并使用新建项目向导进行配置(仅当资源库中不存在.project工程文件时才可用,意思是如果代码库中有了这个工程文件,那么它就认为这是一个信息完整的工程,在导入的过程中就不需要 ...

随机推荐

  1. windows10下 Jupyter 添加anaconda环境

    参考:https://blog.csdn.net/weixin_39934500/article/details/79138235 首先查看 anaconda下的环境信息  conda env lis ...

  2. php中增删改查以及返回结果(一)

    虽然毕业后找的第一份正式的工作并不那么令人满意,但是在度过最初的迷茫期后,自己还是决定成为一个程序猿. 最近也是利用上班偶尔闲下来的时间,开始看书,撸代码,写一些小程序. 这两个礼拜主要的写的都是有关 ...

  3. 2.eclipse安装

    1.进入官网https://www.eclipse.org/ 2.配置工作目录:存放 1.项目代码    2.IDE相关配置信息 3.没有配置tomcat,所以为空.

  4. 【2017-06-17】QtGui基础控件:QSpinBox及QDoubleSpinBox

    今天开始一个新的系列,主要是翻译并摘录QtGui中基础空间的常用方法,并做一些简单的实验程序: 我觉得这是一个炒冷饭的行为,但有时候冷饭不能不炒,不热不好吃,而且也很容易发霉. 其实到现在这种状态,对 ...

  5. OpenGL纹理高级

    矩形纹理 对于二维纹理来说,除了GL_TEXTURE_2D之外,使用GL_TEXTURE_RECTANGLE就可以使用矩形纹理. 矩形纹理几大特点: 不能Mip,只能加载glTexImage2D的le ...

  6. 你真的会用ABAP, Java和JavaScript里的constructor么?

    如果constructor里调用了一个成员方法,这个方法被子类override了,当初始化一个子类实例时,父类的构造函数被的调用,此时父类构造函数的上下文里调用的成员方法,是父类的实现还是子类的实现? ...

  7. *1 Two Sum two pointers(hashmap one scan)

    Given an array of integers, return indices of the two numbers such that they add up to a specific ta ...

  8. Xapian简明教程(未完成)

    第一章 简介 1.1 简介 Xapian是一个开源的搜索引擎库,它可以让开发者自定义的开发一些高级的的索引和查找因素应用在他们的应用中. 通过阅读这篇文档,希望可以帮助你创建第一个你的索引数据库和了解 ...

  9. 微信的 rpx

    微信小程序新单位rpx与自适应布局   rpx是微信小程序新推出的一个单位,按官方的定义,rpx可以根据屏幕宽度进行自适应,在rpx出现之前,web页面的自适应布局已经有了多种解决方案,为什么微信还捣 ...

  10. linux下安装mysql并修改密码

    删除已有mysql并重新安装mysql 查看是否已安装过mysql rpm -qa |grep -i mysql 2.移除安装的包 (在之前如果有启动mysql最好关掉服务) 使用rpm –ev 包名 ...