如何在sc.textFile中加载本地文件,而不是HDFS
如何在sc.textFile中加载本地文件,而不是HDFS
我正在跟随那个很棒的Spark教程,所以我在46分00秒尝试加载README.md
,但是失败了。我做的是这样的:\n
$ sudo docker run -i -t -h sandbox sequenceiq/spark:1.1.0 /etc/bootstrap.sh -bash bash-4.1# cd /usr/local/spark-1.1.0-bin-hadoop2.4 bash-4.1# ls README.md README.md bash-4.1# ./bin/spark-shell scala> val f = sc.textFile("README.md") 14/12/04 12:11:14 INFO storage.MemoryStore: ensureFreeSpace(164073) called with curMem=0, maxMem=278302556 14/12/04 12:11:14 INFO storage.MemoryStore: Block broadcast_0 stored as values in memory (estimated size 160.2 KB, free 265.3 MB) f: org.apache.spark.rdd.RDD[String] = README.md MappedRDD[1] at textFile at:12 scala> val wc = f.flatMap(l => l.split(" ")).map(word => (word, 1)).reduceByKey(_ + _) org.apache.hadoop.mapred.InvalidInputException: Input path does not exist: hdfs://sandbox:9000/user/root/README.md at org.apache.hadoop.mapred.FileInputFormat.singleThreadedListStatus(FileInputFormat.java:285)
\n我该如何加载那个README.md
?
在使用sc.textFile加载本地文件而不是HDFS时,问题的出现原因是文件位于Spark主节点上。解决方法有两种:
1. 在本地模式下启动spark-shell,然后使用file://前缀加载文件。
$ spark-shell --master=local scala> val df = spark.read.json("file:///usr/lib/spark/examples/src/main/resources/people.json") df: org.apache.spark.sql.DataFrame = [age: bigint, name: string] scala> df.show() +----+-------+ | age| name| +----+-------+ |null|Michael| | 30| Andy| | 19| Justin| +----+-------+
2. 可以先将文件从本地文件系统复制到HDFS,然后在其默认模式下启动Spark(例如,在使用AWS EMR时使用YARN),直接读取文件。
$ hdfs dfs -mkdir -p /hdfs/spark/examples $ hadoop fs -put /usr/lib/spark/examples/src/main/resources/people.json /hdfs/spark/examples $ hadoop fs -ls /hdfs/spark/examples Found 1 items -rw-r--r-- 1 hadoop hadoop 73 2017-05-01 00:49 /hdfs/spark/examples/people.json $ spark-shell scala> val df = spark.read.json("/hdfs/spark/examples/people.json") df: org.apache.spark.sql.DataFrame = [age: bigint, name: string] scala> df.show() +----+-------+ | age| name| +----+-------+ |null|Michael| | 30| Andy| | 19| Justin| +----+-------+
以上两种方法是解决这个问题的方法之一。
问题的出现原因是:在使用sc.textFile加载本地文件时,有些用户会将文件路径写成$SPARK_HOME/file:///path/to/file
这样的形式,以为file:///
表示当前用户的home目录,然而实际上file:///
表示的是执行JVM看到的文件系统的根目录。
解决方法是:将文件路径写成file:///path/to/file
的形式,其中file:///
表示的是执行JVM看到的文件系统的根目录。
下面是整理后的
在使用sc.textFile
加载本地文件时,有些用户会将文件路径写成$SPARK_HOME/file:///path/to/file
这样的形式,以为file:///
表示当前用户的home目录,然而实际上file:///
表示的是执行JVM看到的文件系统的根目录。
file:///
是执行JVM看到的文件系统的根目录,而不是当前用户的home目录。根据RFC 8089中规定的URI格式,file://hostname/absolute/path
,在本地情况下,hostname
(authority)部分为空。
因此,解决方法是将文件路径写成file:///path/to/file
的形式,其中file:///
表示执行JVM看到的文件系统的根目录。希望这个解决方法能够帮助像我这样的新手节省一些时间。
在Spark中加载本地文件,而不是HDFS文件,可以使用sc.textFile("file:///path to the file/")
来显式指定路径。出现错误的原因是Hadoop环境的设置。SparkContext.textFile在内部调用org.apache.hadoop.mapred.FileInputFormat.getSplits
,该方法再调用org.apache.hadoop.fs.getDefaultUri
,如果没有指定协议,则使用该方法读取Hadoop配置中的"fs.defaultFS"参数。如果设置了HADOOP_CONF_DIR环境变量,该参数通常设置为"hdfs://...",否则设置为"file://"。
有人问如何在Java中实现这一功能,他发现没有相应的方法,因此对于从简单文件系统加载文件的路径设置非常困扰。有人回答说,可以使用spark-submit命令的--file选项来传递文件路径,这样可以在提交任务时指定路径,以便执行器可以看到路径。
还某些情况下在Windows上指定路径时,"file:///C:\\Xiang\\inputfile
和"file:////C:\\Xiang\\inputfile
都可以工作,而"file://C:\\Xiang\\inputfile
在Java代码中不起作用。在Linux上应该如何设置路径前缀呢?是file:///
(三个斜杠)还是file:////
(四个斜杠)?file:////
在Linux上也能工作吗?
检查源代码后发现,static final URI NAME = URI.create("file:///");
,因此路径前缀应该硬编码为file:///
(三个斜杠)。但我仍然不明白为什么file:////
(四个斜杠)也能工作。
有人询问是否可以在Java代码中添加指向源代码行的链接,提供了一个指向源代码的GitHub链接。
还某些情况下他的错误消息:java.lang.IllegalArgumentException: Wrong FS: file://C:\Xiang\cs_hdfs\csByDate\20190822/C:/Xiang/323Bit/bigfoot, expected: file:/// at org.apache.hadoop.fs.FileSystem.checkPath(FileSystem.java:645) at org.apache.hadoop.fs.RawLocalFileSystem.pathToFile(RawLocalFileSystem.java:80) at org.apache.hadoop.fs.RawLocalFileSystem.deprecatedGetFileStatus(RawLocalFileSystem.java:534) at org.apache.hadoop.fs.RawLocalFileSystem.getFileLinkStatusInternal(RawLocalFileSystem.java:752)
有人说他没有Windows电脑,并且有人询问如何在Java代码中检查是否设置了Hadoop环境。