大数据开发之Spark篇----spark-core入门(5)

Spark序列化&Spark配置读取

序列化

简单案例

我们之前读取的文件都是文本文件,所以我们是使用textFile这个算子来读取文件所有的路径的,但是如果我们要读取的是一个序列化后的文件,我们就不能使用这个算子来读取了。我们就该用sequenceFile这个算子。

val file = sc.sequenceFile[BytesWritable,String]("")
    val end = file.map(x => (x._1.copyBytes(),x._2)).map(x => x._2.split("\t")).count()
    println(end)

从上面的代码中我们可以看到当我们使用sequenceFile算子读取的时候,我们需要定义清楚读取到的key-valueRDD中键和值的数据类型,一般我们都使用BytesWritable这个类型来标记key和String来标记value。因为这个BytesWritable是一个二进制的类型,如果我们想读取里面的内容需要对这个可以使用.copyBytes()方法来转化,但是一般我们都不需要这个头信息的,所以我们去掉就好了,上面我只是显示一下怎么操作而已。
如果你使用textFile来读取sequencefile文件的话,你将没办法正确读取出里面的内容的。

Spark里面的两种序列化器

在Spark里面是默认使用java的序列化器的,使用了java里面的ObjectOutputStream这个类来将对象序列化到一个写入流里面,这个就可以将我们的对象传输出去了。当然了,我们的类也是要实现了Serialization这个接口的,熟悉java SE的小伙伴应该知道的。
这个序列化器的优点是可以序列化所有的对象,但是其也面临这序列化后体积臃肿的缺点,所以我们一般是使用第二个序列化器的。
Kryo序列化器是一个比java序列化器更快,更紧凑(前提是你已经在这个序列化器里注册了你要序列化的类,否则它会直接序列化你要用的这个类所在的包里面,可能体积还会更大)的序列化器。
这种方式是在代码当中设置的

val conf = new SparkConf().set("spark.serializer","org.apache.spark.serializer.KryoSerializer")
val sc = new SparkContext(conf)

看到这个key的名称,大家应该想到这种其实是可以在spark-submit/spark-shell后面设置,或者将其写到spark-default.xml里面。

spark-submit \
--name xxx \
--master local[2] \
--class xxxx \
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
--conf spark.yarn.jars=hdfs://doudou:8020/user/spark_jars \
xxxx.jar

上面这种方式就是外部传入的方式

但我们要是要对某些外部变量进行序列化的时候并且我们是使用kryo的方式来序列化的话,我们需要先将这个外部变量的类注册到conf当中去,否则在序列化的时候将会把这个类所在的包全部序列化了,这个序列化后可能更加大了,效果还不如java的那个了。
我们在这里将A这个类注册了,在注册方法里面传入的是一个数组,数组的元素是classOf[T],T是类名(泛型)

case class A(val id:Int){}

object SequenceFile {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().set(("spark.serializer","org.apache.spark.serializer.KryoSerializer").registerKryoClasses(Array(classOf[A]))
    val sc = new SparkContext(conf)
    val file = sc.sequenceFile[BytesWritable,String]("")
    val end = file.map(x => (x._1.copyBytes(),x._2)).map(x => x._2).count()
    println(end)
  }
}

Spark读取配置

SparkSubmitArguments

我们先来看看这个类,这个类是我们使用spark-submit这个脚本的时候读取配置属性的,该类注释已经说了
在这里插入图片描述
我们可以看到在这个类里面定义了所有的属性,当我们使用spark-shell/spark-submit来执行时,从外部定义的属性参数都是使用了这个类来定义的。
里面有一个这样的方法来读取环境变量中的值:

/**
   * Load arguments from environment variables, Spark properties etc.
   */
  private def loadEnvironmentArguments(): Unit = {
    master = Option(master)
      .orElse(sparkProperties.get("spark.master"))
      .orElse(env.get("MASTER"))
      .orNull
    driverExtraClassPath = Option(driverExtraClassPath)
      .orElse(sparkProperties.get("spark.driver.extraClassPath"))
      .orNull
    driverExtraJavaOptions = Option(driverExtraJavaOptions)
      .orElse(sparkProperties.get("spark.driver.extraJavaOptions"))
      .orNull
    driverExtraLibraryPath = Option(driverExtraLibraryPath)
      .orElse(sparkProperties.get("spark.driver.extraLibraryPath"))
      .orNull
    driverMemory = Option(driverMemory)
      .orElse(sparkProperties.get("spark.driver.memory"))
      .orElse(env.get("SPARK_DRIVER_MEMORY"))
      .orNull
    driverCores = Option(driverCores)
      .orElse(sparkProperties.get("spark.driver.cores"))
      .orNull
    executorMemory = Option(executorMemory)
      .orElse(sparkProperties.get("spark.executor.memory"))
      .orElse(env.get("SPARK_EXECUTOR_MEMORY"))
      .orNull
    executorCores = Option(executorCores)
      .orElse(sparkProperties.get("spark.executor.cores"))
      .orElse(env.get("SPARK_EXECUTOR_CORES"))
      .orNull
    totalExecutorCores = Option(totalExecutorCores)
      .orElse(sparkProperties.get("spark.cores.max"))
      .orNull
    name = Option(name).orElse(sparkProperties.get("spark.app.name")).orNull
    jars = Option(jars).orElse(sparkProperties.get("spark.jars")).orNull
    files = Option(files).orElse(sparkProperties.get("spark.files")).orNull
    pyFiles = Option(pyFiles).orElse(sparkProperties.get("spark.submit.pyFiles")).orNull
    ivyRepoPath = sparkProperties.get("spark.jars.ivy").orNull
    ivySettingsPath = sparkProperties.get("spark.jars.ivySettings")
    packages = Option(packages).orElse(sparkProperties.get("spark.jars.packages")).orNull
    packagesExclusions = Option(packagesExclusions)
      .orElse(sparkProperties.get("spark.jars.excludes")).orNull
    repositories = Option(repositories)
      .orElse(sparkProperties.get("spark.jars.repositories")).orNull
    deployMode = Option(deployMode)
      .orElse(sparkProperties.get("spark.submit.deployMode"))
      .orElse(env.get("DEPLOY_MODE"))
      .orNull
    numExecutors = Option(numExecutors)
      .getOrElse(sparkProperties.get("spark.executor.instances").orNull)
    queue = Option(queue).orElse(sparkProperties.get("spark.yarn.queue")).orNull
    keytab = Option(keytab).orElse(sparkProperties.get("spark.yarn.keytab")).orNull
    principal = Option(principal).orElse(sparkProperties.get("spark.yarn.principal")).orNull
    dynamicAllocationEnabled =
      sparkProperties.get("spark.dynamicAllocation.enabled").exists("true".equalsIgnoreCase)

    // Try to set main class from JAR if no --class argument is given
    if (mainClass == null && !isPython && !isR && primaryResource != null) {
      val uri = new URI(primaryResource)
      val uriScheme = uri.getScheme()

      uriScheme match {
        case "file" =>
          try {
            Utils.tryWithResource(new JarFile(uri.getPath)) { jar =>
              // Note that this might still return null if no main-class is set; we catch that later
              mainClass = jar.getManifest.getMainAttributes.getValue("Main-Class")
            }
          } catch {
            case _: Exception =>
              error(s"Cannot load main class from JAR $primaryResource")
          }
        case _ =>
          error(
            s"Cannot load main class from JAR $primaryResource with URI $uriScheme. " +
            "Please specify a class through --class.")
      }
    }

    // Global defaults. These should be keep to minimum to avoid confusing behavior.
    master = Option(master).getOrElse("local[*]")

    // In YARN mode, app name can be set via SPARK_YARN_APP_NAME (see SPARK-5222)
    if (master.startsWith("yarn")) {
      name = Option(name).orElse(env.get("SPARK_YARN_APP_NAME")).orNull
    }

    // Set name from main class if not given
    name = Option(name).orElse(Option(mainClass)).orNull
    if (name == null && primaryResource != null) {
      name = new File(primaryResource).getName()
    }

    // Action should be SUBMIT unless otherwise specified
    action = Option(action).getOrElse(SUBMIT)
  }

里面的sparkProperties是一个HashMap记录了所有的环境变量的值,下面这个方法就是将默认的配置信息加载到sparkProperties

 /**
   * Merge values from the default properties file with those specified through --conf.
   * When this is called, `sparkProperties` is already filled with configs from the latter.
   */
  private def mergeDefaultSparkProperties(): Unit = {
    // Use common defaults file, if not specified by user
    propertiesFile = Option(propertiesFile).getOrElse(Utils.getDefaultPropertiesFile(env))
    // Honor --conf before the defaults file
    defaultSparkProperties.foreach { case (k, v) =>
      if (!sparkProperties.contains(k)) {
        sparkProperties(k) = v
      }
    }
  }

然后,这个类就直接调用了两个方法,当调用这个类的时候就直接执行了下面的代码了。

mergeDefaultSparkProperties()
  // Remove keys that don't start with "spark." from `sparkProperties`.
  ignoreNonSparkProperties()
  // Use `sparkProperties` map along with env vars to fill in any missing parameters
  loadEnvironmentArguments()

Utils.scala读取spark默认配置文件的方法

就是这个方法了,其使用传入一个env,这个就是sys.env用来获取$SPARK_HOME的地址然后再从里面读取对应的conf目录下的spark-default.xml的路径

/** Return the path of the default Spark properties file. */
  def getDefaultPropertiesFile(env: Map[String, String] = sys.env): String = {
    env.get("SPARK_CONF_DIR")
      .orElse(env.get("SPARK_HOME").map { t => s"$t${File.separator}conf" })
      .map { t => new File(s"$t${File.separator}spark-defaults.conf")}
      .filter(_.isFile)
      .map(_.getAbsolutePath)
      .orNull
  }

生产上使用的一些小习惯

那么从这里我们引出了一些有趣的问题,我们可以在代码或者配置文件当中获取一些spark.*开头的属性,那么我们是不是可以将一些我们想用的属性配置到里面去呢?比如:如果我们要连接外部数据库的url,username,password等以spark开头写到代码中或者配置文件中,这样就方便很多了嘛。如果我们所要配的属性没有用spark开头的话,spark是会自动忽略的,注意第二行代码的注释 Remove keys that don’t start with “spark.” from sparkProperties

mergeDefaultSparkProperties()
  // Remove keys that don't start with "spark." from `sparkProperties`.
  ignoreNonSparkProperties()
  // Use `sparkProperties` map along with env vars to fill in any missing parameters
  loadEnvironmentArguments()

猜你喜欢

转载自blog.csdn.net/weixin_39702831/article/details/85252737