Spark进行一些控制、行动操作练习,并查阅源码加深理解

1.数据来源于搜狗实验室
数据为2006年8月份的日志数据

2.大致查看下包内文件 tar -ztf SogouQ.tar.gz

root@host1:/usr/wh# tar -ztf SogouQ.tar.gz
SogouQ/
SogouQ/access_log.20060801.decode.filter
SogouQ/access_log.20060802.decode.filter
SogouQ/access_log.20060803.decode.filter
SogouQ/access_log.20060804.decode.filter
SogouQ/access_log.20060805.decode.filter
SogouQ/access_log.20060806.decode.filter
SogouQ/access_log.20060807.decode.filter
SogouQ/access_log.20060808.decode.filter
SogouQ/access_log.20060809.decode.filter
SogouQ/access_log.20060810.decode.filter

3.解压上传至hdfs

  • tar -zxf SogouQ.tar.gz
  • sudo -uhdfs hdfs dfs -mkdir -p /user/test/test_data/sougou
  • 上传数据sudo -uhdfs hdfs dfs -put /tmp/tmp2018511/access_log.20060801.decode.filter /user/test/test_data/sougou
  • 报错:有一台datanode的50010拒绝访问【0010是datanode用于数据交换的服务端口】
    java.io.IOException: Got error, status message , ack with firstBadLink as 172.**.***.**:50010
    注意,这里仅一台机器报错,不代表文件上传失败,只能说拒绝连接的那台datanode上没有副本

    1. 查看了已添加的Iptables规则iptables -L -n -v,50010并未开放
    2. 添加规则,并指定可以链接的IP地址:
      iptables -A INPUT -p tcp --dport 50010 -s 172.**.***.** -j ACCEPT
      /etc/rc.d/init.d/iptables save
      service iptables restart

      尝试只开放INPUT,失败,加上OUTPUT,仍然失败
    3. 删除刚才添加的规则:
      iptables -L -n --line-numbers【将所有iptables以序号标记显示】
      iptables -D INPUT 7【删除INPUT中的7】
    4. 尝试使用-p all
      iptables -A INPUT -p all -s 172.**.***.** -j ACCEPT
    5. 再次上传仍然失败,TODO【暂时关闭防火墙,上传完毕再打开】
-A-I-p–dport-s-j
于规则链末尾追加规则指定序号添加规则,默认为1,也就是规则链的最前面代表协议类型,比如tcp,udp,icmp等等,忽略的话,则允许所有协议。也可以使用-p all端口号指定source address表示匹配时如何做,是DROP则拒绝访问,ACCEPT允许链接

INPUT 表示”别人”对“我”的访问,OUTPUT表示“我”的输出

可惜,数据和官方说明的格式对不上,但是搜狗新闻数据又是一堆小文件。先不管了,先用着

# 这是日志数据
6383203565086312    [bt????]    8 1 www.lovetu.com/
07822362349231865   [??????]    3 1 ldjiamu.blog.sohu.com/10491955.html
23528656921072266   [http://onlyasianmovies.net]    1 1 onlyasianmovies.net/

4.SparkSession拿到sparkContext对象,读取hdfs文件,显示指定创建5个partitions

val sparkSession:SparkSession = SparkSession.builder.appName("SougouQ").master("spark://host1.com:7077").getOrCreate
val sc = sparkSession.sparkContext
val sougouSource:RDD[String] = sc.textFile("hdfs:/user/test/test_data/sougou/access_log.20060801.decode.filter",5)

还可以这样同时读取多个文件:
val rdd = sparkContext.wholeTextFile(“hdfs://a-hdfs-path”)
more:sequenceFile、newApiHadoopFile等等
详见SparkContext源码

5.简单看下SparkSession的源码实现

  • 最外层SparkSession类的构造器
@InterfaceStability.Stable
class SparkSession private(
    @transient val sparkContext: SparkContext,
    @transient private val existingSharedState: Option[SharedState],
    @transient private val parentSessionState: Option[SessionState],
    @transient private[sql] val extensions: SparkSessionExtensions)
  extends Serializable with Closeable with Logging {
  // Closeable是Java接口;Serializable/Logging是特质(trait)接口,Serializable底层就是Serializable的Java接口
  // Option[SharedState]和Option[SessionState]代表这两个参数是可选的,有可能为空
  // Option有Some,None两个子类;
  // Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None
  // @InterfaceStability.Unstable是说,接口不稳定 
  • builder()函数的实现

    只有一行 def builder(): Builder = new Builder
    Builder是SparkSession的伴生对象object SparkSession的一个内部类class Builder extends Logging
    【该内部类负责:参数设置,启用Hive支持,创建SparkSession】
    【伴生对象是在第一次访问的时候初始化,不可以new,不能带参数】

  • SparkSession中参数的设置

    private[this] val options = new scala.collection.mutable.HashMap[String, String]
    所有的参数,都以key-value的方式存储在HashMap中,比如options += key -> value.toString
    也可以直接传递一个SparkConfigdef config(conf: SparkConf): Builder
    【使用此方法设置的参数会自动传播到SparkConf和SparkSession自己的配置中。】

    • 所以可以使用builder.config(“key”, value)配置参数
    • 也可以创建SparkSession之后可以设置运行参数,代码如下:
      // 设置一些运行时参数
      spark.conf.set(“spark.sql.shuffle.partitions”, 6)
  • getOrCreate方法【SparkContext也有一个getOrCreate方法】

/**
     * Gets an existing [[SparkSession]] or, if there is no existing one, creates a new
     * one based on the options set in this builder.
     *
     * 进行两次检测,
     * 首先检查是否有一个有效的线程本地SparkSession,如果是,返回这一个.
     * 然后它检查是否有一个有效的全局默认SparkSession,如果是,返回这一个.
     * 如果没有有效的全局缺省SparkSession,该方法将创建一个新的SparkSession,并将新创建的SparkSession分配为全局默认值.
     *
     * 如果返回现有的SparkSession,则该构建器中指定的配置选项将应用于当下SparkSession。
     * This method first checks whether there is a valid thread-local SparkSession,
     * and if yes, return that one. It then checks whether there is a valid global
     * default SparkSession, and if yes, return that one. If no valid global default
     * SparkSession exists, the method creates a new SparkSession and assigns the
     * newly created SparkSession as the global default.
     *
     * In case an existing SparkSession is returned, the config options specified in
     * this builder will be applied to the existing SparkSession.
     *
     * @since 2.0.0
     */
    def getOrCreate(): SparkSession = synchronized {
      // 如果有,从当前线程活跃的 session 中获取SparkSession
      // 使用Java中的线程实现,get()内部使用Entry实现
      var session = activeThreadSession.get()
      if ((session ne null) && !session.sparkContext.isStopped) {
        // 为session设置参数
        options.foreach { case (k, v) => session.sessionState.conf.setConfString(k, v) }
        if (options.nonEmpty) {
          // 如果使用已经存在的SparkSession,则我们自己builder时传递的参数可能不会起作用
          logWarning("Using an existing SparkSession; some configuration may not take effect.")
        }
        return session
      }

      // Global synchronization so we will only set the default session once.
      SparkSession.synchronized {
        // If the current thread does not have an active session, get it from the global session.
        // 如果当前线程不存在一个活动的SparkSession,那么就尝试从全局获取,为第二次检测
        session = defaultSession.get()
        if ((session ne null) && !session.sparkContext.isStopped) {
          options.foreach { case (k, v) => session.sessionState.conf.setConfString(k, v) }
          if (options.nonEmpty) {
            logWarning("Using an existing SparkSession; some configuration may not take effect.")
          }
          return session
        }

        // No active nor global default session. Create a new one
        // 没有活动的或者全局默认的SparkSession,就新建一个
        val sparkContext = userSuppliedContext.getOrElse {
          // set app name if not given,没设置appName,则使用UUID生成唯一标志变量(时间+机器识别号等)自动配置
          val randomAppName = java.util.UUID.randomUUID().toString
          val sparkConf = new SparkConf()
          options.foreach { case (k, v) => sparkConf.set(k, v) }
          if (!sparkConf.contains("spark.app.name")) {
            sparkConf.setAppName(randomAppName)
          }
          // SparkContext也有一个getOrCreate方法.
          /** 直接调用 SparkContext的伴生类创建sc */
          val sc = SparkContext.getOrCreate(sparkConf)
          // maybe this is an existing SparkContext, update its SparkConf which maybe used
          // by SparkSession
          options.foreach { case (k, v) => sc.conf.set(k, v) }
          if (!sc.conf.contains("spark.app.name")) {
            sc.conf.setAppName(randomAppName)
          }
          sc
        }

        // Initialize extensions if the user has defined a configurator class.
        val extensionConfOption = sparkContext.conf.get(StaticSQLConf.SPARK_SESSION_EXTENSIONS)
        if (extensionConfOption.isDefined) {

object SparkSession中还有很多管理session的方法。

  • 启用Hive支持
/**
     * Enables Hive support, including connectivity to a persistent Hive metastore, support for
     * Hive serdes, and Hive user-defined functions.
     *
     * 启用hive支持,包括连接到一个持久的hive元数据,支持hive serdes,以及hive的定义函数
     * @since 2.0.0
     */
    def enableHiveSupport(): Builder = synchronized {
      // 检查是否有Hive相关类以及相关配置类.
      if (hiveClassesArePresent) {
        config(CATALOG_IMPLEMENTATION.key, "hive")
      } else {
        throw new IllegalArgumentException(
          "Unable to instantiate SparkSession with Hive support because " +
            "Hive classes are not found.")
      }
    }

6.查看分区数sougouSource.partitions.size关于分区的原则和理由见这篇文章
还可以将数据重新分区。通过对RDD调用.repartition(numPartitions)

7.查看宽窄依赖

  • MapPartitionsRDD var fileds = sougouSource.flatMap(_.split("\t")).map((_,1))
    flatMap和map主要在数组,集合这些数据结构中体现区别,前者
  • fileds.dependencies.foreach{ dep => println($"这个是窄依赖=====${dep.getClass}")}

    这个是窄依赖=====class org.apache.spark.OneToOneDependency

  • ShuflleRDD val reduce = fileds.reduceByKey(_+_)

    这个是宽依赖=====class org.apache.spark.ShuffleDependency

8.RDD分区函数
分区划分对Shuffle极其重要,Spark Partitioner中有哈希分区器HashPartitioner和范围分区器RangePartitioner
reduce.partitioner Partitioner只存在于(K,V)RDD 中,非(K,V)RDD为None【option类型】

Option[org.apache.spark.Partitioner] = Some(org.apache.spark.HashPartitioner@5)

9.打印RDD元素:
reduce.collect程序运行出问题了:
18088端口,Spark的history UI
这里写图片描述

  • yarn8088端口显示任务Finished,但是spark history server18088端口显示任务还有一stage RUNNING
  • 无论kill spark相关kid,还是 yarn application -kill application_1525953647678_0008结果不变,但是yarn显示Application application_1525953647678_0008 has already finished
  • 甚至重启Spark服务依旧RUNNING状态。。。

常见的习惯用法是尝试使用rdd.foreach(println)或rdd.map(println)打印RDD的元素。在单台机器上,这将产生预期的输出并打印所有RDD的元素。但是,在集群模式下,由执行程序的标准输出输出现在写入执行程序的stdout,而不是驱动程序的标准输出,因此驱动程序的stdout不会显示这些!要打印驱动程序中的所有元素,可以使用collect()方法首先将RDD带到驱动程序节点:rdd.collect().foreach(println)。但是,这可能会导致驱动程序内存不足,因为collect()会将整个RDD提取到单台计算机;如果您只需要打印RDD的几个元素,则更安全的方法是使用take():rdd.take(100).foreach(println)。

  • 数据仅仅66MB,机器内存48G,standalong模式,感觉不太可能是内存不足的原因
  • 18088中查看executors,显示还有活跃的Task,查看日志,有心跳Warning

    WARN netty.NettyRpcEndpointRef: Error sending message [message = Heartbeat(5,[Lscala.Tuple2;@9fdf1da,BlockManagerId(5, ip13, 36338))] in 2 attempts

  • 但是yarn中的history job很完整,spark history server中仅仅部分job,spark history server不太完善?

  • 整个集群重启,仍然RUNNING状态,我感觉history server在骗我。。。
  • TODO

10.验证cache缓存
flatmap.cache()
flatmap.count()
// 第一次是从HDFS读取数据
flatmap.count() // 从缓存中读取数据

可以看到,第一次计算耗时10秒,第二次仅耗时0.1秒
这里写图片描述
cache()相当于StorageLevel为MEMORY_ONLY时的persist()
StorageLevel详见StorageLevel的单例伴生对象StorageLevel

11.mapValues【针对value进行map】
val map_values = fileds.mapValues(_ + "@") 在每个value后加上字符@

12.设置checkPoint
sc.setCheckpointDir("hdfs:/user/test/test_data/checkpoint")
map_values.checkpoint()

行动操作

集合标量行动操作

first()、take(num:Int)、count()、collect()、reduce()、top(num:Int)、takeOrdered(num:Int)、aggreate()、fold()、lookup()、countByKey()、foreach() 、foreachPartition()、sortBy()

1.top、takeOrdered
var rdd = sc.makeRDD(Seq(1 to 100))
top降序,takeOrdered相反
var rdd = sc.makeRDD(Seq(3,5,8.8,2,1,7))
rdd.top()

2.lookup
var reverse = flatmap.map(x => (x._2,x._1))
reverse.lookup(1)

TODO

1.——1.3中的防火墙问题
2.——8中分区器的具体学习
3.——9中collet引发的RUNNING不结束,kill不了的问题

这有一篇简单介绍SparkSession使用的文章
RDD_partitions优化

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值