Header

Body

食用指南

环境

一些神奇的环境变量
export HADOOP_HOME=D:\Users\Dell\.hadoop
export HADOOP_USER_NAME=root

在我们的开发环境中,Hadoop 集群使用的是 Simple 认证。即,你说你是谁,Hadoop 就认为你是谁。Spark 默认使用的是系统的环境变量中的 USERNAME 来获取用户名,这里也可以通过设置 HADOOP_USER_NAME 变量来调整用户名。

代码

最简单的 Hadoop 操作代码
// 构建应用
val configuration = new SparkConf()
  .setMaster("local[*]")
  .setAppName("Koi")

val context = SparkContext.getOrCreate(configuration)

context.hadoopConfiguration.set("fs.defaultFS", "hdfs://172.18.38.198:8020")
// 构建文件系统
val fileSystem = FileSystem.get(context.hadoopConfiguration)
// 访问文件系统
val outer = fileSystem.create(
  new Path("hdfs://172.18.38.198:8020/$/20231006.txt")
)

outer.write(response.body)

outer.close()

请注意,在这个例子中,第 8 行是很关键的一行,这一行设置了默认文件系统的位置,这样后续才可以访问这个文件系统。

当然,除了使用代码配置外,你还可以选择直接将 Hadoop 的配置文件复制到程序的资源目录下来实现自动配置。网络上有很多教程,这里就不再赘述。

命令

Spark 任务提交命令
# 这个命令是这么构成的 spark-submit --master ${谁来运行} --class ${运行谁} ${要运行的类从哪来} ${应用的运行参数}
spark-submit --master yarn --class tech.giant.zoo.fish.koi.Launcher ./koi_2.13-0.1.0-SNAPSHOT.jar updateSites

问题

常规问题汇总分析

Exception in thread "main" java.lang.NoSuchMethodError: scala.runtime.RichInt$.to$extension(II)Lscala/collection/immutable/Range$Inclusive;

image.png

这是因为打包出来的 Jar 包中没有携带依赖,遇到这种情况有两种方案处理

  1. 把所有依赖包都复制到服务器上,然后在 spark-submit 时使用 --jar 参数携带这些依赖
  2. 打包 Fat Jar,直接把所有依赖包都带上

做为一个大懒蛋,我肯定选择 Creating a Fat JAR Using SBT | Baeldung on Scala 啦。

分享一下我的项目配置:

ThisBuild / scalaVersion := "2.13.12"
ThisBuild / version := "Build20231006.GJY"
ThisBuild / assemblyMergeStrategy := { name => MergeStrategy.preferProject }

libraryDependencies ++= Seq(
  "org.scalaj" %% "scalaj-http" % "2.4.2",
  "org.apache.spark" %% "spark-core" % "3.5.0" % "provided"
)

lazy val root = (project in file("."))
  .settings(
    name := "Koi",
    idePackagePrefix := Some("tech.giant.zoo.fish.koi"),
    assembly / mainClass := Some("tech.giant.zoo.fish.koi.Main"),
  )

好好好,还是报相同的错误。这时候我仔细看了下报错信息,发现在堆栈的最底部,SparkSubmit 也正在使用 Scala。我强烈怀疑是 Spark 集群的 Scala 版本与我本地开发所使用的版本不一致导致的错误,于是我便查看了 Spark 使用的 Scala 版本。

image.png

还真是版本不对,😂,抓紧换一下本地的试试。

~其实我们最终的解决方案是升级了下 CDP 集群~

离谱问题大赏

File has reached the limit on maximum number of blocks (dfs.namenode.fs-limits.max-blocks-per-file): 10000 >= 10000
23/10/08 01:59:21 ERROR logging.DriverLogger$DfsAsyncWriter: Failed writing driver logs to dfs
org.apache.hadoop.ipc.RemoteException(java.io.IOException): File has reached the limit on maximum number of blocks (dfs.namenode.fs-limits.max-blocks-per-file): 10000 >= 10000
        at org.apache.hadoop.hdfs.server.namenode.FSDirWriteFileOp.validateAddBlock(FSDirWriteFileOp.java:186)
        at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.getAdditionalBlock(FSNamesystem.java:2815)
        at org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.addBlock(NameNodeRpcServer.java:874)
        at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolServerSideTranslatorPB.addBlock(ClientNamenodeProtocolServerSideTranslatorPB.java:589)
        at org.apache.hadoop.hdfs.protocol.proto.ClientNamenodeProtocolProtos$ClientNamenodeProtocol$2.callBlockingMethod(ClientNamenodeProtocolProtos.java)
        at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.call(ProtobufRpcEngine.java:533)
        at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:1070)
        at org.apache.hadoop.ipc.Server$RpcCall.run(Server.java:989)
        at org.apache.hadoop.ipc.Server$RpcCall.run(Server.java:917)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:422)
        at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1898)
        at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2894)
        at org.apache.hadoop.ipc.Client.getRpcResponse(Client.java:1562)
        at org.apache.hadoop.ipc.Client.call(Client.java:1508)
        at org.apache.hadoop.ipc.Client.call(Client.java:1405)
        at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:233)
        at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:118)
        at com.sun.proxy.$Proxy13.addBlock(Unknown Source)
        at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.addBlock(ClientNamenodeProtocolTranslatorPB.java:523)
        at sun.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:431)
        at org.apache.hadoop.io.retry.RetryInvocationHandler$Call.invokeMethod(RetryInvocationHandler.java:166)
        at org.apache.hadoop.io.retry.RetryInvocationHandler$Call.invoke(RetryInvocationHandler.java:158)
        at org.apache.hadoop.io.retry.RetryInvocationHandler$Call.invokeOnce(RetryInvocationHandler.java:96)
        at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:362)
        at com.sun.proxy.$Proxy14.addBlock(Unknown Source)
        at org.apache.hadoop.hdfs.DFSOutputStream.addBlock(DFSOutputStream.java:1122)
        at org.apache.hadoop.hdfs.DataStreamer.locateFollowingBlock(DataStreamer.java:1880)
        at org.apache.hadoop.hdfs.DataStreamer.nextBlockOutputStream(DataStreamer.java:1682)
        at org.apache.hadoop.hdfs.DataStreamer.run(DataStreamer.java:719)

我算是总结出来一个道理,程序崩溃必然选择三更半夜。

这个崩溃的原因是因为 Spark 会把 driver 的日志输出存在 Hadoop /usr/spark/driverLogs 目录下。我们这是一个爬虫程序,运行了一段时间后由于输出了大量的日志导致超过了 Hadoop 单文件大小限制于是便出现了上述问题。

在程序启动时我发现了这条日志,也证明了这个问题。

23/10/08 11:55:57 INFO logging.DriverLogger$DfsAsyncWriter: Started driver log file sync to: /user/spark/driverLogs/local-1696766155850_driver.log

重启程序,解决问题。

Footer

最后修改:2023 年 10 月 08 日
如果觉得我的文章对你有用,请随意赞赏