Spark 版本定制 第1课:通过案例实战对 SparkStreaming 透彻理解之一

Spark源码版本定制从SparkStreaming切入的原因:

1,早期版本的Spark只有Spark Core,在后续版本的演进中逐渐增加了Spark SQL、Spark Streaming、Spark R、Spark GraphX、Spark MLlib等框架。Spark Streaming 是Spark Core上的一个子框架,我们通过对一个子框架的彻底研究,一定能够掌握对所有Spark框架问题的解决之道;

2,Spark SQL涉及了太多的SQL语法细节的解析和优化,对于我们集中精力研究Spark而言不太适合;Spark R很不成熟,支持功能有限;Spark GraphX最近的几个版本没有新的进展,而且涉及到很多数学级别的算法;Spark MLlib也涉及了太多的数学知识;而Spark Streaming是流式计算,在大数据流处理时代,一切不能进行实时流处理的数据,都是无效的数据;Spark Streaming的流式处理,可以在线的使用机器学习、图计算、Spark SQL、Spark R的成果,这得益于Spark的一体化、多元化的技术架构的设计,这是Spark无可匹敌之处,这也是Spark Streaming必将一统大数据天下的根源。

本期内容:

1,Spark Streaming另类在线实验

2,瞬间理解Spark Streaming本质

一、Spark Streaming另类在线实验

        我们在学习和研究Spark Streaming的过程中,经常会有一些比较困惑的问题:例如说数据不断的流进来,不断的生成Job并提交给集群进行处理,我们渴望更清晰的看到数据的流入、渴望更清晰的看到数据被处理的过程。如果能看得很清楚的话,心里就会很踏实。其实只需要使用一个小技巧,就是把Spark Streaming 的Batch Interval放到足够大,例如说5分钟一次(Batch Interval时间间隔的大小对于我们而言没有太大的影响,但是对于我们理解Spark Streaming的工作原理具有重大的意义),把处理的过程放慢,以方便看清楚程序执行的各个环节。

我们从已经写过的广告计费系统中在线黑名单过滤实战案例的Spark Streaming程序入手。

本案例中Spark集群测试环境有5个节点:Master、Worker1、Worker2、Worker3、Worker4。

Spark版本为最新的 spark-1.6.1

本案例源代码如下:

package com.dt.spark.sparksteaming

import org.apache.spark.SparkConf
import org.apache.spark.streaming.StreamingContext
import org.apache.spark.streaming.Seconds

object OnlineBlackListFilter {
    def main(args: Array[String]){
      /**
       * 第1步:创建Spark的配置对象SparkConf,设置Spark程序的运行时的配置信息,
       * 例如说通过setMaster来设置程序要链接的Spark集群的Master的URL,如果设置
       * 为local,则代表Spark程序在本地运行,特别适合于机器配置条件非常差(例如
       * 只有1G的内存)的初学者       *
       */
      val conf = new SparkConf() //创建SparkConf对象
      conf.setAppName("OnlineBlackListFilter") //设置应用程序的名称,在程序运行的监控界面可以看到名称
      conf.setMaster("spark://Master:7077") //此时,程序在Spark集群

      val ssc = new StreamingContext(conf, Seconds(30))
     
      /**
       * 黑名单数据准备,实际上黑名单一般都是动态的,例如在Redis或者数据库中,黑名单的生成往往有复杂的业务
       * 逻辑,具体情况算法不同,但是在Spark Streaming进行处理的时候每次都能工访问完整的信息
       */
      val blackList = Array(("hadoop", true),("mahout", true))
      val blackListRDD = ssc.sparkContext.parallelize(blackList, 8)
     
      val adsClickStream = ssc.socketTextStream("Master", 9999)
     
      /**
       * 此处模拟的广告点击的每条数据的格式为:time、name
       * 此处map操作的结果是name、(time,name)的格式
       */
      val adsClickStreamFormatted = adsClickStream.map { ads => (ads.split(" ")(1), ads) }
      adsClickStreamFormatted.transform(userClickRDD => {
        //通过leftOuterJoin操作既保留了左侧用户广告点击内容的RDD的所有内容,又获得了相应点击内容是否在黑名单中
        val joinedBlackListRDD = userClickRDD.leftOuterJoin(blackListRDD)
       
        /**
         * 进行filter过滤的时候,其输入元素是一个Tuple:(name,((time,name), boolean))
         * 其中第一个元素是黑名单的名称,第二元素的第二个元素是进行leftOuterJoin的时候是否存在在值
         * 如果存在的话,表面当前广告点击是黑名单,需要过滤掉,否则的话则是有效点击内容;
         */
        val validClicked = joinedBlackListRDD.filter(joinedItem => {
          if(joinedItem._2._2.getOrElse(false))
          {
            false
          } else {
            true
          }
         
        })
       
        validClicked.map(validClick => {validClick._2._1})
      }).print
     
      /**
       * 计算后的有效数据一般都会写入Kafka中,下游的计费系统会从kafka中pull到有效数据进行计费
       */
      ssc.start()
      ssc.awaitTermination()
    }
}

把Batch Interval的时间间隔重新设置为300秒,代码如下:

val ssc = new StreamingContext(conf, Seconds(300))

然后把编译好的程序导出成一个jar文件,部署到Master节点的某个目录下。

启动start-dfs.sh、Spark sbin目录下的start-all.sh start-history-server.sh三个脚本文件。

通过 nc -lk 9999 命令打开Socket发送数据的端口。

到Spark bin目录下执行 spark-submit 命令提交刚才打包的 jar文件到Spark集群。

向Socket发送数据的端口输入部分测试数据:

165136_88rh_2755933.png

程序正常运行并在控制台打印出计算后的结果:

171407_lnTt_2755933.jpg

我们立即停止程序的执行,此时从实际执行的角度讲只有一个Job(Batch级别)。

打开浏览器,输入http://Master:18080,查看Web UI上的History Server的日志信息如下:

173728_KEne_2755933.jpg

应用程序列表中最后一次执行的程序则为我们刚才执行完成的程序,可以看到该程序运行了2分钟,点击该程序超链接进去以后可以看到共有5个已完成的Job,且Job 1的运行时间为1.5分钟,而从实际执行的角度讲只有一个Job:

174722_6T4b_2755933.png

点击Job 0超链接进去以后从DAG Visualization图中可以看到,此Job并没有执行业务逻辑的代码,从中可以发现Spark Streaming在运行的过程中会自动额外的启动一些Job,这是我们从Web控制台看到的第一个现象

190752_PhEp_2755933.png

另外还可以看到,Job 0跨两个Stage,点击Stage 0超链接进去后,会发现有50个已完成的任务:

192709_fS17_2755933.png

从Aggregated Metrics by Executor 处可以看到,此Stage 跨四个Executor:

193129_sOls_2755933.png

回过头来再点击Job 1超链接,进去后发现此Job只有一个Stage 2:

194724_2lXA_2755933.png

点击Stage 2 超链接进去后,从DAG Visualization图中可以看到,makeRDD在ReceiverTracker上执行,ReceiverTracker是整个集群的数据追踪器。而且从Aggregated Metrics by Executor 处可以看到,此Stage只在Worker4节点上执行,且执行时间为1.5分钟:

195219_WgNM_2755933.png

在前面我们已经看到过,整个程序运行了2分钟,那么在这里是什么程序运行了1.5分钟?

是数据接收器Receiver运行了1.5分钟,因为Receiver在一直不断地接收数据,且Receiver是在Executor上运行的,SparkStreaming 启动Receiver是通过一个Job来执行的,是这个Job(也就是我们看到的Job 1)运行了1.5分钟,在一个具体Cluster的Worker上,Executor中启动Receiver是通过一个Job来执行的(肯定有一个Action触发),而且这个Receiver只在一个Executor中运行,且在这个地方就是通过一个Task接收数据,这是我们从Web控制台看到的第二个现象。从这个角度来讲,Receiver接收数据和普通的Job没有区别,由此我们也得到一个启发,就是我们在一个Spark Application中可以启动很多个Job,不同的Job之间可以相互配合。通过SparkStreaming框架默认启动的Job,接收数据为后续的处理做准备,为我们写复杂程序奠定了非常良好的基础,这就是我们写非常复杂的Spark程序的一个黄金切入点。

另外还有一点,从上图中可以看到,Tasks的 Locality Level 为 PROCESS_LOCAL,PROCESS_LOCAL 为内存节点,SparkStreaming 默认接收数据的方式为 MEMORY_AND_DISK_SER_2,再次说明一点,数据量比较少内存能放的下的情况下数据不会放入磁盘,处理的时候直接使用内存中的数据。

再回去看Job 2的运行情况,发现此Job有三个Stage在执行:

114016_59Td_2755933.png

115132_2bs4_2755933.png

在Stage 3上,有一个BlockRDD 来自于 socket text stream的方法,其实就是InputDStream通过BatchInterval时间间隔产生的RDD。

点击Stage 3超链接进去,查看该Stage运行情况,发现此Stage在4个Executor上执行:

115517_UNd2_2755933.png

115905_5nwH_2755933.png

这里进行的是transform操作,发生在四个节点的Executor上,这就说明,接收数据是在一个节点上,处理数据是在4个节点上,最大化地利用集群资源进行处理。

点击Stage 4超链接进去,查看该Stage运行情况,发现此Stage也是在4个Executor上执行:

130933_jvSy_2755933.png

点击Stage 5超链接进去,查看该Stage运行情况,发现此Stage只发生在Worker4节点上:

131232_zWIl_2755933.png

131409_kRiD_2755933.png

这里进行了leftOuterJoin操作,会产生一个结果,把结果放在Worker4节点上。

再回去看Job 3的运行情况,发现此Job跳过了两个Stage,实际上只有一个Stage 8 在执行:

132257_EDsA_2755933.png

132459_d2IT_2755933.png

点击Stage 8 超链接进去,发现此Stage也是发生在4个节点上:

132908_JSRK_2755933.png

最后再看Job 4 的运行情况,发现此Job也是跳过了两个Stage,实际上只有一个Stage 11 在执行:

133420_mkct_2755933.png

133549_9Jt6_2755933.png

点击Stage 11 超链接进去,发现此Stage发生在3个节点上:

133909_Mnkn_2755933.png

综合以上可以发现,执行一个Batch级别的Job,实际上会产生很多个Job在执行,这会告诉我们一件事情,就是SparkStreaming 绝不是网络和书籍上看到的那么简单!

二、瞬间理解Spark Streaming本质

SparkStreaming 本身是按照时间间隔把流进来的数据划分成Job,触发Job在Cluster上执行的一个流式处理引擎,它实际上是加上了时间维度的批处理。

例如说我们刚才的程序是5分钟产生一批数据,基于这一批数据会生成RDD,基于RDD就会触发Job,至于RDD的生成、Job的生成及触发,都是框架在做的事情。

下面请看一幅图:

143946_x9sb_2755933.png

为了便于理解,我把它拆分成下面的四张小图:

144638_qBWw_2755933.png

这张图表明:Spark Streaming 可以接收来自于 Kafka、Flume、HDFS/S3 和 Kinesis 等不同来源的实时数据,按照特定的逻辑进行实时计算处理后,处理结果又可以存储到 HDFS、数据库、仪表盘等各种介质上。

144641_dxj3_2755933.png

这张图说明:Spark Streaming接收这些实时输入数据流,并将它们按照批次划分,然后交给Spark引擎处理后,生成对应批次的数据流。

144641_SN2b_2755933.png

这张图说明:Spark Streaming 提供了表示连续数据流的、高度抽象的被称为离散流的 DStream。DStream 本质上就是 RDD的序列,DStream 中的每个 RDD 都包含来自一个时间间隔的数据。

144642_8Shd_2755933.png

这张图说明:Spark Streaming 除了可以使用不同数据来源的输入流创建DStream外,也可以在已有的DStream的基础上使用一些操作来创建新的DStream。任何对DStream的操作都会转变为对底层RDD的操作。本图例子是对lines DStream进行了flatMap操作后,生成words DStream。

DStream 是一个没有边界的集合,没有大小的限制,DStream 代表了时空的概念。随着时间的推移,里面不断的产生 RDD,DStream 就是 RDD 的集合,只不过有时间的先后顺序。空间维度是 DStream 的处理层面,我们对 DStream 的处理其实就作用在 RDD 上,所以 DStream 就是时空的代表。时间如果固定的话,就会锁定空间的操作,这个操作其实就是 transform,也就是对本时间段内对应批次的数据的处理。

对 DStream 的操作会构建 DStream Graph,下面展示另一张图:

155803_ggFP_2755933.png

图中左侧为程序代码,构建了两个InputDStream,代表两个数据输入来源,它们进行了 join 操作把数据联合起来,产生了Join DStream,Join DStream又进行了 map 操作,产生了 Mapped DStream,Mapped DStream又分别进行了三次操作:

1、先进行了 map 操作,又进行了 foreach 操作,产生了 Foreach DStream;

2、直接进行了 foreach 操作,产生了 Foreach DStream;

3、先进行了 reduce 操作,产生了 Reduce DStream,又进行了 foreach 操作,产生了 Foreach DStream。

在本例中,每个 foreach 操作会触发作业,就会从后往前回溯,这就形成了 DStream 之间的依赖关系,也就构建了 DStream Graph,如右侧图中所示。

下面展示另一张图:

162327_1kfd_2755933.png

DStream Graph 是模板,对它进行操作加上时间维度就会实例化并生成 RDD Graph。

空间维度确定以后,随着时间的不断推进,就会把空间维度的 DStream Graph 实例化成 RDD Graph,然后触发具体Job的执行。

现在再去阅读 Spark Streaming 的官方文档,就好理解多了。

164628_dM8X_2755933.png

备注:

更多私密内容,请关注微信公众号:DT_Spark

更多干货请访问微博:http://weibo.com/ilovepains

每晚20:00大数据Spark技术永久免费公开课,YY频道:68917580


转载于:https://my.oschina.net/dongjunyi/blog/672341

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值