其中的奧妙在於使用了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