配置Tez引擎时,只要我们把Tez的jars和配置文件路径添加进HADOOP_CLASSPATH里,然后通过设置mapred-site.xml文件的mapreduce.framework.name属性为yarn-tez,就可以替换默认的MR框架为tez引擎。
其中的奥妙在于使用了JDK6+的一个特性ServiceLoader类。其为JDK实现了一个依赖注入的机制。
ServiceLoader可以在加载jar包的时候,构建jar包配置的指定接口的类实现对象,从而提供一个插件实现的作用。具体到这个mapreduce.framework.name属性的实现,在hadoop-mapreduce-client-jobclient-x.x.x.jar包里,路径META-INF\services下,有一个org.apache.hadoop.mapreduce.protocol.ClientProtocolProvider的文件,里面有这样一行
YarnClientProtocolProvider的create是这样实现的。
其中的奥妙在于使用了JDK6+的一个特性ServiceLoader类。其为JDK实现了一个依赖注入的机制。
ServiceLoader可以在加载jar包的时候,构建jar包配置的指定接口的类实现对象,从而提供一个插件实现的作用。具体到这个mapreduce.framework.name属性的实现,在hadoop-mapreduce-client-jobclient-x.x.x.jar包里,路径META-INF\services下,有一个org.apache.hadoop.mapreduce.protocol.ClientProtocolProvider的文件,里面有这样一行
org.apache.hadoop.mapred.YarnClientProtocolProvider
类似地,tez-mapreduce也提供了这样的文件,内容为
org.apache.tez.mapreduce.client.YarnTezClientProtocolProvider
在Cluster类初始化的时候,private static ServiceLoader frameworkLoader = ServiceLoader.load(ClientProtocolProvider.class);
这样在frameworkLoader就把这两个类加载起来。注意到ServiceLoader的实现了Iterable接口,内部返回的iterator实现了延迟和缓存机制。
然后会调用initialize方法。
private void initialize(InetSocketAddress jobTrackAddr, Configuration conf)
throws IOException {
synchronized (frameworkLoader) {
for (ClientProtocolProvider provider : frameworkLoader) {
LOG.debug("Trying ClientProtocolProvider : "
+ provider.getClass().getName());
ClientProtocol clientProtocol = null;
try {
if (jobTrackAddr == null) {
clientProtocol = provider.create(conf);
} else {
clientProtocol = provider.create(jobTrackAddr, conf);
}
if (clientProtocol != null) {
clientProtocolProvider = provider;
client = clientProtocol;
LOG.debug("Picked " + provider.getClass().getName()
+ " as the ClientProtocolProvider");
break;
}
else {
LOG.debug("Cannot pick " + provider.getClass().getName()
+ " as the ClientProtocolProvider - returned null protocol");
}
}
catch (Exception e) {
LOG.info("Failed to use " + provider.getClass().getName()
+ " due to error: " + e.getMessage());
}
}
}
if (null == clientProtocolProvider || null == client) {
throw new IOException(
"Cannot initialize Cluster. Please check your configuration for "
+ MRConfig.FRAMEWORK_NAME
+ " and the correspond server addresses.");
}
}
YarnClientProtocolProvider的create是这样实现的。
public class YarnClientProtocolProvider extends ClientProtocolProvider {
@Override
public ClientProtocol create(Configuration conf) throws IOException {
if (MRConfig.YARN_FRAMEWORK_NAME.equals(conf.get(MRConfig.FRAMEWORK_NAME))) {
return new YARNRunner(conf);
}
return null;
}
@Override
public ClientProtocol create(InetSocketAddress addr, Configuration conf)
throws IOException {
return create(conf);
}
@Override
public void close(ClientProtocol clientProtocol) throws IOException {
// nothing to do
}
}
这样就实现了插件类的方法。
参考文章:how mapreduce.framework.name is hooked into YARN MR2