在前一篇文章Hadoop源码解析之YARN客户端作业提交流程中,介绍了创建客户端代理阶段用到java.util.ServiceLoader加载
YarnClientProtocolProvider和LocalClientProtocolProvider。
以前从来没有使用过这个类,进去大概看了一下具体的实现。主要是从META-INF/services这个目录下的配置文件加载给定接口或者基类的实现,
ServiceLoader会根据给定的类的full name来在META-INF/services下面找对应的文件,在这个文件中定义了所有这个类的子类或者接口的实现类
,返回一个实例。
下面以一个具体的例子来说明一下ServiceLoader的具体使用,类似Hadoop中的实现。
首先定义一个接口,具体如下:
package ServiceLoaderTest;
public interface MyServiceInterface {
public String SayHello();
}
该接口有两个子类,分别为ServiceA和ServiceB:
package ServiceLoaderTest;
public class ServiceA implements MyServiceInterface{
public String SayHello(){
return "I am ServiceA.";
}
}
package ServiceLoaderTest;
public class ServiceB implements MyServiceInterface{
public String SayHello() {
return "I am ServiceB.";
}
}
需要在META-INF/services下以MyServiceInterface这个类的全名来新建立一个文件,文件中的内容为两个实现类的全名,
其中包名是ServiceLoaderTest,如下:
ServiceLoaderTest.ServiceA
ServiceLoaderTest.ServiceB
所有的实现和配置都已经完成,下面写一个测试类来看一下结果:
package ServiceLoaderTest;
import java.util.ServiceLoader;
public class Test {
public static void main(String[] args)
{
ServiceLoader<MyServiceInterface> sl = ServiceLoader.load(MyServiceInterface.class);
for(MyServiceInterface s : sl)
{
System.out.println(s.SayHello());
}
}
}
具体的输出来如下:
I am ServiceA.
I am ServiceB.
测试用例文件的文件结构:
可以看到ServiceLoader可以根据MyServiceInterface把定义的两个实现类找出来,返回一个ServiceLoader的实现,
而ServiceLoader实现了Iterable接口,所以可以通过ServiceLoader来遍历所有在配置文件中定义的类的实例。
Hadoop很多地方就是通过这个机制来根据不同文件的配置来返回不同的具体实现类。
hadoop中Cluster类的相关实现如下:
public class Cluster {
......
private static ServiceLoader<ClientProtocolProvider> frameworkLoader =
ServiceLoader.load(ClientProtocolProvider.class);
......
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.");
}
}
META-INF/services的位置为: