Spark中常用工具类Utils的简明介绍

来源:互联网 发布:宁波大学网络课程平台 编辑:程序博客网 时间:2024/04/29 02:05

《深入理解Spark:核心思想与源码分析》一书前言的内容请看链接《深入理解SPARK:核心思想与源码分析》一书正式出版上市

《深入理解Spark:核心思想与源码分析》一书第一章的内容请看链接《第1章 环境准备》

《深入理解Spark:核心思想与源码分析》一书第二章的内容请看链接《第2章 SPARK设计理念与基本架构》

《深入理解Spark:核心思想与源码分析》一书第三章第一部分的内容请看链接《深入理解Spark:核心思想与源码分析》——SparkContext的初始化(伯篇)》

《深入理解Spark:核心思想与源码分析》一书第三章第二部分的内容请看链接《深入理解Spark:核心思想与源码分析》——SparkContext的初始化(仲篇)》

《深入理解Spark:核心思想与源码分析》一书第三章第三部分的内容请看链接《深入理解Spark:核心思想与源码分析》——SparkContext的初始化(叔篇)》

《深入理解Spark:核心思想与源码分析》一书第三章第四部分的内容请看链接《深入理解Spark:核心思想与源码分析》——SparkContext的初始化(季篇)》


Utils是Spark中最常用的工具类之一,如果不关心其实现,也不会对理解Spark有太多影响。但是对于Scala或者Spark的初学者来说,通过了解Utils工具类的实现,也是个不错的入门途径。下面将逐个介绍Utils工具类提供的常用方法。

1.localHostName

功能描述:获取本地机器名。


[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def localHostName(): String = {  
  2.     customHostname.getOrElse(localIpAddressHostname)  
  3.   }  

2.getDefaultPropertiesFile

功能描述:获取默认的Spark属性文件。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def getDefaultPropertiesFile(env: Map[String, String] = sys.env): String = {  
  2.   env.get("SPARK_CONF_DIR")  
  3.     .orElse(env.get("SPARK_HOME").map{ t => s"$t${File.separator}conf"})  
  4.     .map { t => new File(s"$t${File.separator}spark-defaults.conf")}  
  5.     .filter(_.isFile)  
  6.     .map(_.getAbsolutePath)  
  7.     .orNull  
  8. }  

3.loadDefaultSparkProperties

功能描述:加载指定文件中的Spark属性,如果没有指定文件,则加载默认Spark属性文件的属性。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def loadDefaultSparkProperties(conf:SparkConf, filePath: String = null):String = {  
  2.   val path =Option(filePath).getOrElse(getDefaultPropertiesFile())  
  3.   Option(path).foreach { confFile =>  
  4.     getPropertiesFromFile(confFile).filter{ case (k,v) =>  
  5.       k.startsWith("spark.")  
  6.     }.foreach { case (k, v) =>  
  7.      conf.setIfMissing(k, v)  
  8.      sys.props.getOrElseUpdate(k, v)  
  9.     }  
  10.   }  
  11.   path  
  12. }  

4.getCallSite

功能描述:获取当前SparkContext的当前调用堆栈,将栈里最靠近栈底的属于spark或者Scala核心的类压入callStack的栈顶,并将此类的方法存入lastSparkMethod;将栈里最靠近栈顶的用户类放入callStack,将此类的行号存入firstUserLine,类名存入firstUserFile,最终返回的样例类CallSite存储了最短栈和长度默认为20的最长栈的样例类。在JavaWordCount例子中,获得的数据如下:

  • 最短栈:JavaSparkContext at JavaWordCount.java:44;
  • 最长栈:org.apache.spark.api.java.JavaSparkContext.<init>(JavaSparkContext.scala:61)org.apache.spark.examples.JavaWordCount.main(JavaWordCount.java:44)。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def getCallSite(skipClass: String => Boolean =coreExclusionFunction): CallSite = {  
  2.     val trace =Thread.currentThread.getStackTrace().filterNot { ste: StackTraceElement=>  
  3.       ste == null || ste.getMethodName == null || ste.getMethodName.contains("getStackTrace")  
  4.     }  
  5.     var lastSparkMethod= "<unknown>"  
  6.     var firstUserFile= "<unknown>"  
  7.     var firstUserLine= 0  
  8.     var insideSpark= true  
  9.     var callStack= new ArrayBuffer[String]() :+ "<unknown>"  
  10.    
  11.     for (el<- trace) {  
  12.       if (insideSpark){  
  13.         if (skipClass(el.getClassName)){  
  14.           lastSparkMethod = if(el.getMethodName == "<init>") {  
  15.             el.getClassName.substring(el.getClassName.lastIndexOf('.') + 1)  
  16.           } else {  
  17.             el.getMethodName  
  18.           }  
  19.           callStack(0) = el.toString // Putlast Spark method on top of the stack trace.  
  20.         } else {  
  21.           firstUserLine = el.getLineNumber  
  22.           firstUserFile = el.getFileName  
  23.           callStack += el.toString  
  24.           insideSpark = false  
  25.         }  
  26.       } else {  
  27.         callStack += el.toString  
  28.       }  
  29.     }  
  30.     val callStackDepth= System.getProperty("spark.callstack.depth","20").toInt  
  31.     CallSite(  
  32.       shortForm = s"$lastSparkMethod at $firstUserFile:$firstUserLine",  
  33.       longForm = callStack.take(callStackDepth).mkString("\n"))  
  34.   }  

5.startServiceOnPort

功能描述:Scala跟其它脚本语言一样,函数也可以传递,此方法正是通过回调startService这个函数来启动服务,并最终返回startService返回的service地址及端口。如果启动过程有异常,还会多次重试,直到达到maxRetries表示的最大次数。

 

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def startServiceOnPort[T](  
  2.      startPort:Int,  
  3.      startService:Int => (T, Int),  
  4.      conf:SparkConf,  
  5.      serviceName:String = ""): (T, Int) = {  
  6.   require(startPort == 0 || (1024 <= startPort && startPort < 65536),  
  7.      "startPort should be between 1024 and 65535(inclusive), or 0 for a random free port.")  
  8.    val serviceString= if (serviceName.isEmpty) "" elses" '$serviceName'"  
  9.    val maxRetries= portMaxRetries(conf)  
  10.    for (offset<- 0 to maxRetries){  
  11.      val tryPort= if (startPort == 0) {  
  12.        startPort  
  13.      } else {  
  14.        ((startPort+ offset - 1024)% (65536 - 1024))+ 1024  
  15.      }  
  16.      try {  
  17.        val (service,port) = startService(tryPort)  
  18.        logInfo(s"Successfullystarted service$serviceString on port $port.")  
  19.        return (service,port)  
  20.      } catch {  
  21.        case e:Exception if isBindCollision(e) =>  
  22.          if (offset>= maxRetries) {  
  23.            val exceptionMessage=  
  24.              s"${e.getMessage}:Service$serviceString failed after $maxRetries retries!"  
  25.            val exception= new BindException(exceptionMessage)  
  26.            exception.setStackTrace(e.getStackTrace)  
  27.            throw exception  
  28.          }  
  29.         logWarning(s"Service$serviceString couldnot bind on port $tryPort. " +  
  30.            s"Attempting port ${tryPort+ 1}.")  
  31.      }  
  32.    }  
  33.    throw newSparkException(s"Failed to start service$serviceString on port$startPort")  
  34.  }  

6.createDirectory

功能描述:用spark+UUID的方式创建临时文件目录,如果创建失败会多次重试,最多重试10次。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def createDirectory(root: String, namePrefix: String = "spark"): File = {  
  2.   var attempts = 0  
  3.   val maxAttempts = MAX_DIR_CREATION_ATTEMPTS  
  4.   var dir: File = null  
  5.   while (dir == null){  
  6.     attempts += 1  
  7.     if (attempts > maxAttempts) {  
  8.       throw newIOException("Failed to create a temp directory(under " + root + ") after "+  
  9.        maxAttempts + " attempts!")  
  10.     }  
  11.     try {  
  12.       dir = new File(root, "spark-"+ UUID.randomUUID.toString)  
  13.       if (dir.exists() || !dir.mkdirs()) {  
  14.         dir = null  
  15.       }  
  16.     } catch { casee: SecurityException => dir = null;}  
  17.   }  
  18.   
  19.   dir  
  20. }  

7.getOrCreateLocalRootDirs

功能描述:根据spark.local.dir的配置,作为本地文件的根目录,在创建一、二级目录之前要确保根目录是存在的。然后调用createDirectory创建一级目录。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private[spark] defgetOrCreateLocalRootDirs(conf: SparkConf): Array[String] = {  
  2.   if (isRunningInYarnContainer(conf)) {  
  3.    getYarnLocalDirs(conf).split(",")  
  4.   } else {  
  5.    Option(conf.getenv("SPARK_LOCAL_DIRS"))  
  6.      .getOrElse(conf.get("spark.local.dir",System.getProperty("java.io.tmpdir")))  
  7.       .split(",")  
  8.       .flatMap {root =>  
  9.         try {  
  10.           val rootDir = newFile(root)  
  11.           if (rootDir.exists || rootDir.mkdirs()) {  
  12.             val dir = createDirectory(root)  
  13.            chmod700(dir)  
  14.            Some(dir.getAbsolutePath)  
  15.           } else {  
  16.            logError(s"Failed to create dir in $root. Ignoring this directory.")  
  17.             None  
  18.           }  
  19.         } catch {  
  20.           case e: IOException =>  
  21.          logError(s"Failed to create local rootdir in $root. Ignoring this directory.")  
  22.           None  
  23.         }  
  24.       }  
  25.       .toArray  
  26.   }  
  27. }  

8.getLocalDir

功能描述:查询Spark本地文件的一级目录。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def getLocalDir(conf: SparkConf): String = {  
  2.  getOrCreateLocalRootDirs(conf)(0)  
  3. }  

9.createTempDir

功能描述:在Spark一级目录下创建临时目录,并将目录注册到shutdownDeletePaths:scala.collection.mutable.HashSet[String]中。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def createTempDir(  
  2.     root: String= System.getProperty("java.io.tmpdir"),  
  3.     namePrefix:String = "spark"): File = {  
  4.   val dir =createDirectory(root, namePrefix)  
  5.  registerShutdownDeleteDir(dir)  
  6.   dir  
  7. }  

10.RegisterShutdownDeleteDir

功能描述:将目录注册到shutdownDeletePaths:scala.collection.mutable.HashSet[String]中,以便在进程退出时删除。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def registerShutdownDeleteDir(file: File) {  
  2.   val absolutePath =file.getAbsolutePath()  
  3.   shutdownDeletePaths.synchronized{  
  4.     shutdownDeletePaths += absolutePath  
  5.   }  
  6. }  

11.hasRootAsShutdownDeleteDir

功能描述:判断文件是否匹配关闭时要删除的文件及目录,shutdownDeletePaths:scala.collection.mutable.HashSet[String]存储在进程关闭时要删除的文件及目录。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def hasRootAsShutdownDeleteDir(file: File): Boolean = {  
  2.   val absolutePath= file.getAbsolutePath()  
  3.   val retval= shutdownDeletePaths.synchronized {  
  4.     shutdownDeletePaths.exists { path =>  
  5.       !absolutePath.equals(path) && absolutePath.startsWith(path)  
  6.     }  
  7.   }  
  8.   if (retval){  
  9.     logInfo("path = " + file + ", already present as root for deletion.")  
  10.   }  
  11.   retval  
  12. }  

12.deleteRecursively

功能描述:用于删除文件或者删除目录及其子目录、子文件,并且从shutdownDeletePaths:scala.collection.mutable.HashSet[String]中移除此文件或目录。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def deleteRecursively(file: File) {  
  2.   if (file != null){  
  3.     try {  
  4.       if (file.isDirectory &&!isSymlink(file)) {  
  5.         var savedIOException:IOException = null  
  6.         for (child<- listFilesSafely(file)) {  
  7.           try {  
  8.            deleteRecursively(child)  
  9.           } catch {  
  10.             case ioe:IOException => savedIOException = ioe  
  11.           }  
  12.         }  
  13.         if (savedIOException!= null) {  
  14.           throw savedIOException  
  15.         }  
  16.         shutdownDeletePaths.synchronized {  
  17.           shutdownDeletePaths.remove(file.getAbsolutePath)  
  18.         }  
  19.       }  
  20.     } finally {  
  21.       if (!file.delete()) {  
  22.         if (file.exists()) {  
  23.           throw newIOException("Failed to delete: " +file.getAbsolutePath)  
  24.         }  
  25.       }  
  26.     }  
  27.   }  
  28. }  

13.getSparkClassLoader

功能描述:获取加载当前class的ClassLoader。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def getSparkClassLoader = getClass.getClassLoader  

14.getContextOrSparkClassLoader

功能描述:用于获取线程上下文的ClassLoader,没有设置时获取加载Spark的ClassLoader。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def getContextOrSparkClassLoader =  
  2.   Option(Thread.currentThread().getContextClassLoader).getOrElse(getSparkClassLoader)  

15.newDaemonCachedThreadPool

功能描述:使用Executors.newCachedThreadPool创建的缓存线程池。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def newDaemonCachedThreadPool(prefix: String): ThreadPoolExecutor = {  
  2.   val threadFactory =namedThreadFactory(prefix)  
  3.  Executors.newCachedThreadPool(threadFactory).asInstanceOf[ThreadPoolExecutor]  
  4. }  

16.doFetchFile

功能描述:使用URLConnection通过http协议下载文件。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private defdoFetchFile(url: String, targetDir: File, filename: String, conf: SparkConf,  
  2.     securityMgr:SecurityManager, hadoopConf: Configuration) {  
  3.   val tempFile= File.createTempFile("fetchFileTemp",null, newFile(targetDir.getAbsolutePath))  
  4.   val targetFile= new File(targetDir, filename)  
  5.   val uri = new URI(url)  
  6.   val fileOverwrite= conf.getBoolean("spark.files.overwrite",defaultValue = false)  
  7.   Option(uri.getScheme).getOrElse("file")match {  
  8.     case "http""https" | "ftp" =>  
  9.       logInfo("Fetching " + url + " to " + tempFile)  
  10.       var uc:URLConnection = null  
  11.       if (securityMgr.isAuthenticationEnabled()) {  
  12.         logDebug("fetchFile with security enabled")  
  13.         val newuri= constructURIForAuthentication(uri,securityMgr)  
  14.         uc = newuri.toURL().openConnection()  
  15.         uc.setAllowUserInteraction(false)  
  16.       } else {  
  17.         logDebug("fetchFile not using security")  
  18.         uc = newURL(url).openConnection()  
  19.       }  
  20.       valtimeout = conf.getInt("spark.files.fetchTimeout"60) * 1000  
  21.       uc.setConnectTimeout(timeout)  
  22.       uc.setReadTimeout(timeout)  
  23.       uc.connect()  
  24.       val in = uc.getInputStream()  
  25.      downloadFile(url, in, tempFile, targetFile,fileOverwrite)  
  26.     case "file"=>  
  27.       val sourceFile= if (uri.isAbsolute)new File(uri)else newFile(url)  
  28.      copyFile(url, sourceFile, targetFile, fileOverwrite)  
  29.     case _ =>  
  30.       val fs =getHadoopFileSystem(uri, hadoopConf)  
  31.       val in = fs.open(newPath(uri))  
  32.      downloadFile(url, in, tempFile, targetFile,fileOverwrite)  
  33.   }  
  34. }  

17.fetchFile

功能描述:如果文件在本地有缓存,则从本地获取,否则通过HTTP远程下载。最后对.tar、.tar.gz等格式的文件解压缩后,调用shell命令行的chmod命令给文件增加a+x的权限。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def fetchFile(  
  2.     url: String,  
  3.     targetDir:File,  
  4.     conf:SparkConf,  
  5.     securityMgr:SecurityManager,  
  6.     hadoopConf:Configuration,  
  7.     timestamp:Long,  
  8.     useCache:Boolean) {  
  9.   val fileName= url.split("/").last  
  10.   val targetFile= new File(targetDir, fileName)  
  11.   val fetchCacheEnabled= conf.getBoolean("spark.files.useFetchCache",defaultValue = true)  
  12.   if (useCache && fetchCacheEnabled) {  
  13.     val cachedFileName= s"${url.hashCode}${timestamp}_cache"  
  14.     val lockFileName= s"${url.hashCode}${timestamp}_lock"  
  15.     val localDir= new File(getLocalDir(conf))  
  16.     val lockFile= new File(localDir,lockFileName)  
  17.     val raf = new RandomAccessFile(lockFile,"rw")  
  18.     val lock = raf.getChannel().lock()  
  19.     val cachedFile= new File(localDir,cachedFileName)  
  20.     try {  
  21.       if (!cachedFile.exists()){  
  22.        doFetchFile(url, localDir, cachedFileName, conf, securityMgr, hadoopConf)  
  23.       }  
  24.     } finally {  
  25.       lock.release()  
  26.     }  
  27.     copyFile(  
  28.       url,  
  29.       cachedFile,  
  30.       targetFile,  
  31.      conf.getBoolean("spark.files.overwrite",false)  
  32.     )  
  33.   } else {  
  34.    doFetchFile(url, targetDir, fileName,conf, securityMgr, hadoopConf)  
  35.   }  
  36.   if (fileName.endsWith(".tar.gz") || fileName.endsWith(".tgz")) {  
  37.     logInfo("Untarring " + fileName)  
  38.    Utils.execute(Seq("tar""-xzf", fileName),targetDir)  
  39.   } else if(fileName.endsWith(".tar")){  
  40.     logInfo("Untarring " + fileName)  
  41.    Utils.execute(Seq("tar""-xf", fileName),targetDir)  
  42.   }  
  43.   FileUtil.chmod(targetFile.getAbsolutePath, "a+x")  
  44. }  

18.executeAndGetOutput

功能描述:执行一条command命令,并且获取它的输出。调用stdoutThread的join方法,让当前线程等待stdoutThread执行完成。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def executeAndGetOutput(  
  2.     command:Seq[String],  
  3.     workingDir:File = new File("."),  
  4.    extraEnvironment: Map[String, String] = Map.empty): String = {  
  5.   val builder= new ProcessBuilder(command: _*)  
  6.      .directory(workingDir)  
  7.   val environment= builder.environment()  
  8.   for ((key, value) <- extraEnvironment) {  
  9.     environment.put(key,value)  
  10.   }  
  11.   val process= builder.start()  
  12.   new Thread("readstderr for " + command(0)) {  
  13.     override defrun() {  
  14.       for (line<- Source.fromInputStream(process.getErrorStream).getLines()){  
  15.         System.err.println(line)  
  16.       }  
  17.     }  
  18.   }.start()  
  19.   val output= new StringBuffer  
  20.   val stdoutThread= new Thread("readstdout for " + command(0)) {  
  21.     override defrun() {  
  22.       for (line<- Source.fromInputStream(process.getInputStream).getLines()){  
  23.         output.append(line)  
  24.       }  
  25.     }  
  26.   }  
  27.   stdoutThread.start()  
  28.   val exitCode= process.waitFor()  
  29.   stdoutThread.join()   // Wait for itto finish reading output  
  30.   if (exitCode!= 0) {  
  31.     logError(s"Process $commandexited with code $exitCode: $output")  
  32.     throw newSparkException(s"Process $command exited with code $exitCode")  
  33.   }  
  34.   output.toString  
  35. }  

19.memoryStringToMb

功能描述:将内存大小字符串转换为以MB为单位的整型值。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def memoryStringToMb(str: String): Int = {  
  2.   val lower =str.toLowerCase  
  3.   if (lower.endsWith("k")) {  
  4.     (lower.substring(0,lower.length-1).toLong/ 1024).toInt  
  5.   } else if(lower.endsWith("m")){  
  6.     lower.substring(0,lower.length-1).toInt  
  7.   } else if(lower.endsWith("g")){  
  8.     lower.substring(0,lower.length-1).toInt* 1024  
  9.   } else if(lower.endsWith("t")){  
  10.     lower.substring(0,lower.length-1).toInt* 1024 * 1024  
  11.   } else {// nosuffix, so it's just a number in bytes  
  12.     (lower.toLong / 10241024).toInt  
  13.   }  
  14. }  

0 0
原创粉丝点击