Class 在 运行 Hadoop 作业时加载问题
Class loading issue while running a Hadoop job
我有一个 C++ 服务,它公开了 2 个接口:
一个。 Submit():用于向 YARNRM
提交 DistCp 作业
b。 Query():用于查询应用程序的状态。
此服务在内部调用 Java 客户端(通过 JNI),它有 2 个静态函数:
提交()
查询()
提交() 做:
DistCp distCp = new DistCp(configuration, distCpOptions);
Job job = distCp.execute();
Parses the "application ID" from the tracking URL and returns it.
Query() 执行:
Takes "application ID" returned in Submit()
YarnClient yarnClient = YarnClient.createYarnClient();
yarnClient.init(new YarnConfiguration());
yarnClient.start();
yarnClient.getApplicationReport(applicationID);
yarnClient.stop();
我面临的问题是,
- 如果对服务的第一次调用是 Submit(),则所有后续调用(Submit() 和 Query())都会成功
- 但是,如果对服务的第一次调用是 Query(),那么所有 Submit() 调用都会失败。
Query() 调用在所有条件下都成功。
Submit() 调用因错误而失败(下面的第 1 次调用、第 2 次调用和第 3 次调用,有不同的异常):
java.util.ServiceConfigurationError: org.apache.hadoop.mapreduce.protocol.ClientProtocolProvider: Provider org.apache.hadoop.mapred.LocalClientProtocolProvider not found
java.util.ServiceConfigurationError: org.apache.hadoop.mapreduce.protocol.ClientProtocolProvider: Provider org.apache.hadoop.mapred.YarnClientProtocolProvider not found
java.io.IOException: Cannot initialize Cluster. Please check your configuration for mapreduce.framework.name and the correspond server addresses.
我调试了这个问题并发现,当首先调用 Query() API 时,然后 classes LocalClientProtocolProvider
和 YarnClientProtocolProvider
没有被加载。当调用 Submit() 时,class 加载器应该加载这些 classes。但是,这并没有发生。
我还观察到,当首先调用 Query() API 时,Hadoop 配置发生了变化,并包含许多与 "mapreduce.*" 配置相关的默认设置。
我尝试在调用 Submit() 方法后立即使用 Class.forName() 显式加载。但是,这也无济于事。
调用 Submit() 时,为什么 class 加载程序不加载所需的 classes?这是 Hadoop 配置或 Java class 加载器的问题吗?还是因为我正在混合使用 MapReduce 和 Yarn APIs?
"mapreduce.framework.name"配置设置为"yarn".
我的环境是Hadoop 2.6.0.
我的class路径包含以下路径中存在的所有 Hadoop jar:
a. hadoop/common/
b. hadoop/common/lib
c. hadoop/hdfs/
d. hadoop/hdfs/lib
e. hadoop/mapreduce/
f. hadoop/mapreduce/lib
g. hadoop/yarn/
h. hadoop/yarn/lib
我发现,我正在混合使用 Yarn
和 MapReduce
API,这导致了 class 加载问题。
当首先调用Query()
时,它加载所有与YARN相关的classes。
例如:
org.apache.hadoop.yarn.client.api.YarnClient from file:/D:/data/hadoop-2
.6.0-SNAPSHOT/share/hadoop/yarn/hadoop-yarn-client-2.6.0-SNAPSHOT.jar
但是,MapReduce 相关的 classes 没有加载。例如,未加载以下 class:
org.apache.hadoop.mapred.YarnClientProtocolProvider from
file:/D:/data/hdoop-2.6.0-SNAPSHOT/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.6.0-SNAPSHOT.jar
因此,当 Submit()
被调用时,class 加载程序假定它已经加载了所有必需的 classes。但是,classes YarnClientProtocolProvider
和 LocalClientProtocolProvider
尚未加载。因此,Submit()
调用失败。
为了强制 class 加载程序加载所有 MapReduce
相关的 classes,我在 YarnClientWrapper
的构造函数中添加了以下语句(这是一个单例 class 并包装 YarnClient
).
Cluster cluster = new Cluster(configuration);
cluster.getFileSystem();
cluster.close();
这解决了问题。
但是,更简洁的实施方式是在 Query()
中使用 MapReduce
客户端而不是 YarnClient
。这将确保我们不会陷入 class 加载问题。
我有一个 C++ 服务,它公开了 2 个接口:
一个。 Submit():用于向 YARNRM
提交 DistCp 作业b。 Query():用于查询应用程序的状态。
此服务在内部调用 Java 客户端(通过 JNI),它有 2 个静态函数:
提交()
查询()
提交() 做:
DistCp distCp = new DistCp(configuration, distCpOptions);
Job job = distCp.execute();
Parses the "application ID" from the tracking URL and returns it.
Query() 执行:
Takes "application ID" returned in Submit()
YarnClient yarnClient = YarnClient.createYarnClient();
yarnClient.init(new YarnConfiguration());
yarnClient.start();
yarnClient.getApplicationReport(applicationID);
yarnClient.stop();
我面临的问题是,
- 如果对服务的第一次调用是 Submit(),则所有后续调用(Submit() 和 Query())都会成功
- 但是,如果对服务的第一次调用是 Query(),那么所有 Submit() 调用都会失败。
Query() 调用在所有条件下都成功。
Submit() 调用因错误而失败(下面的第 1 次调用、第 2 次调用和第 3 次调用,有不同的异常):
java.util.ServiceConfigurationError: org.apache.hadoop.mapreduce.protocol.ClientProtocolProvider: Provider org.apache.hadoop.mapred.LocalClientProtocolProvider not found
java.util.ServiceConfigurationError: org.apache.hadoop.mapreduce.protocol.ClientProtocolProvider: Provider org.apache.hadoop.mapred.YarnClientProtocolProvider not found
java.io.IOException: Cannot initialize Cluster. Please check your configuration for mapreduce.framework.name and the correspond server addresses.
我调试了这个问题并发现,当首先调用 Query() API 时,然后 classes LocalClientProtocolProvider
和 YarnClientProtocolProvider
没有被加载。当调用 Submit() 时,class 加载器应该加载这些 classes。但是,这并没有发生。
我还观察到,当首先调用 Query() API 时,Hadoop 配置发生了变化,并包含许多与 "mapreduce.*" 配置相关的默认设置。
我尝试在调用 Submit() 方法后立即使用 Class.forName() 显式加载。但是,这也无济于事。
调用 Submit() 时,为什么 class 加载程序不加载所需的 classes?这是 Hadoop 配置或 Java class 加载器的问题吗?还是因为我正在混合使用 MapReduce 和 Yarn APIs?
"mapreduce.framework.name"配置设置为"yarn".
我的环境是Hadoop 2.6.0.
我的class路径包含以下路径中存在的所有 Hadoop jar:
a. hadoop/common/
b. hadoop/common/lib
c. hadoop/hdfs/
d. hadoop/hdfs/lib
e. hadoop/mapreduce/
f. hadoop/mapreduce/lib
g. hadoop/yarn/
h. hadoop/yarn/lib
我发现,我正在混合使用 Yarn
和 MapReduce
API,这导致了 class 加载问题。
当首先调用Query()
时,它加载所有与YARN相关的classes。
例如:
org.apache.hadoop.yarn.client.api.YarnClient from file:/D:/data/hadoop-2
.6.0-SNAPSHOT/share/hadoop/yarn/hadoop-yarn-client-2.6.0-SNAPSHOT.jar
但是,MapReduce 相关的 classes 没有加载。例如,未加载以下 class:
org.apache.hadoop.mapred.YarnClientProtocolProvider from
file:/D:/data/hdoop-2.6.0-SNAPSHOT/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.6.0-SNAPSHOT.jar
因此,当 Submit()
被调用时,class 加载程序假定它已经加载了所有必需的 classes。但是,classes YarnClientProtocolProvider
和 LocalClientProtocolProvider
尚未加载。因此,Submit()
调用失败。
为了强制 class 加载程序加载所有 MapReduce
相关的 classes,我在 YarnClientWrapper
的构造函数中添加了以下语句(这是一个单例 class 并包装 YarnClient
).
Cluster cluster = new Cluster(configuration);
cluster.getFileSystem();
cluster.close();
这解决了问题。
但是,更简洁的实施方式是在 Query()
中使用 MapReduce
客户端而不是 YarnClient
。这将确保我们不会陷入 class 加载问题。