我在Google存储桶中存储了一些文件。这些是我建议的here设置。
spark = SparkSession.builder.\
master("local[*]").\
appName("TestApp").\
config("spark.serializer", KryoSerializer.getName).\
config("spark.jars", "/usr/local/.sdkman/candidates/spark/2.4.4/jars/gcs-connector-hadoop2-2.1.1.jar").\
config("spark.kryo.registrator", GeoSparkKryoRegistrator.getName).\
getOrCreate()
#Recommended settings for using GeoSpark
spark.conf.set("spark.driver.memory", 6)
spark.conf.set("spark.network.timeout", 1000)
#spark.conf.set("spark.driver.maxResultSize", 5)
spark.conf.set
spark._jsc.hadoopConfiguration().set('fs.gs.impl', 'com.google.cloud.hadoop.fs.gcs.GoogleHadoopFileSystem')
# This is required if you are using service account and set true,
spark._jsc.hadoopConfiguration().set('fs.gs.auth.service.account.enable', 'false')
spark._jsc.hadoopConfiguration().set('google.cloud.auth.service.account.json.keyfile', "myJson.json")
path = 'mBucket-c892b51f8579.json'
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = path
client = storage.Client()
name = 'https://console.cloud.google.com/storage/browser/myBucket/'
bucket_id = 'myBucket'
bucket = client.get_bucket(bucket_id)
我可以使用以下内容简单阅读它们:
df = pd.read_csv('gs://myBucket/myFile.csv.gz', compression='gzip')
df.head()
time_zone_name province_short
0 America/Chicago US.TX
1 America/Chicago US.TX
2 America/Los_Angeles US.CA
3 America/Chicago US.TX
4 America/Los_Angeles US.CA
我正在尝试使用pyspark
myTable = spark.read.format("csv").schema(schema).load('gs://myBucket/myFile.csv.gz', compression='gzip')
但出现以下错误
Py4JJavaError: An error occurred while calling o257.load.
: java.lang.NoClassDefFoundError: Could not initialize class com.google.cloud.hadoop.fs.gcs.GoogleHadoopFileSystem
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at org.apache.hadoop.conf.Configuration.getClassByNameOrNull(Configuration.java:2134)
at org.apache.hadoop.conf.Configuration.getClassByName(Configuration.java:2099)
at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:2193)
at org.apache.hadoop.fs.FileSystem.getFileSystemClass(FileSystem.java:2654)
at org.apache.hadoop.fs.FileSystem.createFileSystem(FileSystem.java:2667)
at org.apache.hadoop.fs.FileSystem.access$200(FileSystem.java:94)
at org.apache.hadoop.fs.FileSystem$Cache.getInternal(FileSystem.java:2703)
at org.apache.hadoop.fs.FileSystem$Cache.get(FileSystem.java:2685)
at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:373)
at org.apache.hadoop.fs.Path.getFileSystem(Path.java:295)
at org.apache.spark.sql.execution.streaming.FileStreamSink$.hasMetadata(FileStreamSink.scala:45)
at org.apache.spark.sql.execution.datasources.DataSource.resolveRelation(DataSource.scala:332)
at org.apache.spark.sql.DataFrameReader.loadV1Source(DataFrameReader.scala:223)
at org.apache.spark.sql.DataFrameReader.load(DataFrameReader.scala:211)
at org.apache.spark.sql.DataFrameReader.load(DataFrameReader.scala:178)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:244)
at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
at py4j.Gateway.invoke(Gateway.java:282)
at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
at py4j.commands.CallCommand.execute(CallCommand.java:79)
at py4j.GatewayConnection.run(GatewayConnection.java:238)
at java.lang.Thread.run(Thread.java:748)
答案 0 :(得分:1)
欢迎来到hadoop依赖地狱!
1。使用包装而不是罐子
您的配置基本上是正确的,但是当您将gcs-connector添加为本地jar时,您还需要手动确保其所有依赖项在JVM类路径中均可用。
通常将连接器作为一个软件包添加并让spark处理依赖关系更加容易,因此使用@Override
public int getItemCount() {
if(List != null) {
return List.size();
}
return 0;
}
代替config("spark.jars", "/usr/local/.sdkman/candidates/spark/2.4.4/jars/gcs-connector-hadoop2-2.1.1.jar")
2。管理ivy2依赖项解决问题
当您执行上述操作时,由于maven(用于发布)和ivy2(用于spark用于依赖关系解析)之间的分辨率差异,spark可能会抱怨无法下载某些依赖关系。
通常可以通过使用config('spark.jars.packages', 'com.google.cloud.bigdataoss:gcs-connector:hadoop2-2.1.1')
来简单地让spark忽略未解决的依赖项来解决此问题,因此添加新的配置行,例如
spark.jars.excludes
3。管理类路径冲突
完成此操作后,SparkSession将启动,但文件系统仍然会失败,因为pyspark的标准发行版打包了一个旧版本的guava库,该版本未实现gcs-connector依赖的API。
您需要通过使用以下配置config('spark.jars.excludes','androidx.annotation:annotation,org.slf4j:slf4j-api')
和config('spark.driver.userClassPathFirst','true')
来确保gcs-connector首先找到其预期版本
4。管理依赖冲突
现在,您可能会认为一切正常,但实际上没有,因为默认的pyspark发行版包含hadoop库的2.7.3版本,而gcs-connector版本2.1.1仅依赖于2.8+ API。
现在您的选择是:
5。最后一个有效的配置
假设您要坚持使用pyspark的PyPi或Anaconda最新发行版,则以下配置应该可以正常工作。
我仅包含了与gcs相关的配置,将Hadoop配置直接移至spark配置,并假设您正确设置了GOOGLE_APPLICATION_CREDENTIALS:
config('spark.executor.userClassPathFirst','true')
请注意,gcs-connector版本1.9.17与2.1.1相比具有不同的排除集,因为为什么不这样做...
PS:您还需要确保使用的是Java 1.8 JVM,因为Spark 2.4在新的JVM上不起作用。
答案 1 :(得分:0)
除了@rluta 的好答案之外,您还可以通过将番石榴罐特别放在 extraClassPath 中来替换 userClassPathFirst 行:
spark.driver.extraClassPath=/root/.ivy2/jars/com.google.guava_guava-27.0.1-jre.jar:/root/.ivy2/jars/com.google.guava_failureaccess-1.0.1.jar:/root/.ivy2/jars/com.google.guava_listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar
spark.executor.extraClassPath=/root/.ivy2/jars/com.google.guava_guava-27.0.1-jre.jar:/root/.ivy2/jars/com.google.guava_failureaccess-1.0.1.jar:/root/.ivy2/jars/com.google.guava_listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar
这有点笨拙,因为您需要确切的本地 ivy2 路径,不过您也可以将 jar 下载/复制到更永久的地方。
但是,这减少了其他潜在的依赖冲突,例如与 livy 的冲突,如果 gcs-connector 的 jackson 依赖在类路径前面,则会抛出 java.lang.NoClassDefFoundError: org.apache.livy.shaded.json4s.jackson.Json4sModule
。