Cloud Dataproc访问Cloud Storage报错及凭证配置咨询
一、报错java.lang.NoSuchMethodError: com.google.common.util.concurrent.MoreExecutors.directExecutor()的常见原因
这个错误本质是依赖版本冲突,最常见的场景是Google Cloud Storage(GCS)客户端依赖的Guava库版本,与Spark集群自带的Guava版本不兼容。
具体分析:
你的Spark版本是2.4.4,这个版本的Spark内置了较旧版本的Guava(通常是Guava 14或16系列)。而你引入的google-cloud-storage:1.101.0依赖的Guava版本要新得多,当作业在Dataproc集群上运行时,Spark的类加载器会优先加载集群自带的旧版Guava,导致GCS客户端需要的MoreExecutors.directExecutor()方法找不到——这个方法是在较新的Guava版本中新增的,旧版没有。
验证方式:
你可以通过Maven依赖树排查Guava的冲突:
mvn dependency:tree | grep guava
常见解决方案:
- 使用Maven Shade插件重定位Guava包:把GCS依赖的Guava包重命名为独立的命名空间,彻底避免和Spark自带的Guava冲突。示例Shade配置:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <relocations> <relocation> <pattern>com.google.common</pattern> <shadedPattern>your.custom.prefix.google.common</shadedPattern> </relocation> </relocations> </configuration> </execution> </executions> </plugin> </plugins> </build>
- 调整GCS客户端版本:选择与Spark 2.4.4兼容的
google-cloud-storage版本,比如尝试降级到1.90.0左右(需提前确认版本兼容性)。 - 排除冲突依赖:在
google-cloud-storage依赖中排除Guava,强制使用Spark自带的版本,但这种方式可能导致GCS客户端功能异常,不推荐。
二、Dataproc集群上的GCS凭证配置问题
如果你的Dataproc集群已经拥有目标GCS桶的访问权限,不需要单独配置凭证,原因如下:
- Dataproc集群默认会使用集群关联的服务账户(默认是Compute Engine默认服务账户,或者你创建集群时指定的自定义服务账户)来访问GCS资源。
- 当你调用
StorageOptions.getDefaultInstance().getService()时,GCS客户端会自动从Dataproc集群的环境中获取服务账户的凭证,无需手动设置密钥文件或环境变量。
确认权限的要点:
确保集群的服务账户拥有GCS桶的必要权限(比如storage.objects.list、storage.objects.get等),你可以通过IAM控制台给服务账户添加对应的角色(比如Storage Object Viewer、Storage Admin等)。
附:你提供的相关信息
Maven依赖:
<dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.12</artifactId> <version>2.4.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-storage</artifactId> <version>1.101.0</version> </dependency> </dependencies>
报错代码片段:
import com.google.cloud.storage._ object Test { def main(args: Array[String]): Unit = { val storage = StorageOptions.getDefaultInstance().getService() storage.list("intent_raw") } }
报错堆栈:
Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.util.concurrent.MoreExecutors.directExecutor()Ljava/util/concurrent/Executor;
at com.google.api.gax.retrying.BasicRetryingFuture.(BasicRetryingFuture.java:84)
at com.google.api.gax.retrying.DirectRetryingExecutor.createFuture(DirectRetryingExecutor.java:88)
at com.google.api.gax.retrying.DirectRetryingExecutor.createFuture(DirectRetryingExecutor.java:74)
at com.google.cloud.RetryHelper.run(RetryHelper.java:75)
at com.google.cloud.RetryHelper.runWithRetries(RetryHelper.java:50)
at com.google.cloud.storage.StorageImpl.listBlobs(StorageImpl.java:372)
at com.google.cloud.storage.StorageImpl.list(StorageImpl.java:328)
at ai.mandal.cloud.dataproc.Test$.main(Test.scala:14)
at ai.mandal.cloud.dataproc.Test.main(Test.scala)
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 org.apache.spark.deploy.JavaMainApplication.start(SparkApplication.scala:52)
at org.apache.spark.deploy.SparkSubmit.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:845)
at org.apache.spark.deploy.SparkSubmit.doRunMain$1(SparkSubmit.scala:161)
at org.apache.spark.deploy.SparkSubmit.submit(SparkSubmit.scala:184)
at org.apache.spark.deploy.SparkSubmit.doSubmit(SparkSubmit.scala:86)
at org.apache.spark.deploy.SparkSubmit$$anon$2.doSubmit(SparkSubmit.scala:920)
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:929)
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
内容的提问来源于stack exchange,提问作者inteloid

