【YARN源码阅读】基础模块1:服务(Service)
引子
这是整个YARN源码阅读计划的第一篇。先说说写作的思路,整体上,打算把源码分成两大部分来梳理:1. 基础模块;2. 功能模块。基础模块包括服务,事件模型,状态机,RPC,配置管理以及安全机制等。功能模块则包括大家耳熟能详的RM,NM,Container,AM,Client等。可以认为,YARN的核心功能是由功能模块实现支持的,基础模块则是构成功能模块的基石。因此,想要读懂整个框架,先把基石搞清楚,是很必要的,所谓磨刀不误砍柴工。
另一方面,通常读代码时的思路其实是从入口函数出发,跟着调用栈逐个往下研究,所以在整个系列的最后,我也会专门写一个总结篇,从服务启动的视角,将上面提到的模块化的东西进行串联。
目标模块
进入今天的主题:服务(Service)。
如果你读过YARN的一些源码,就会发现其中的好多功能类都继承于AbstractService或者CompositeService。这两个被广泛使用的类,存放路径为
./hadoop-release-2.7.0/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/service
这里注意到的一点,代码父路径是hadoop-common-project,也就是说,其实这个service模块被设计出来,不只是服务于YARN工程,而是针对整个HADOOP项目。
这个模块下的文件其实不多,如下:
.../service $ tree
.
|-- AbstractService.java
|-- CompositeService.java
|-- LifecycleEvent.java
|-- LoggingStateChangeListener.java
|-- package-info.java
|-- Service.java
|-- ServiceOperations.java
|-- ServiceStateChangeListener.java
|-- ServiceStateException.java
|-- ServiceStateModel.java
类图关系
上面陈列的代码中,主要有两组类图关系和一些工具类。其中Service/AbstractService/CompositeService为一组,ServiceStateChangeListener/LoggingStateChangeListener为一组。类图关系如下:
这里先介绍比较简单的一些类的功能:
ServiceStateModel: 服务的状态机模型。定义了uninited,inited,started,stopped等四种状态,并规定了他们之间的可转换关系,并提供了诸如getState,isInState等状态管理方法。
LifecycleEvent:记录引起状态变化的事件,只有两个属性,state和time,即要转变为的状态及发生的时间。
ServiceStateException: 异常类。
ServiceOperations: 工具类,可以用来停止服务,此外包含一个内部类ServiceListeners,用来管理服务的监听器,比如添加,删除,通知等。
ServiceStateChangeListener: 接口,服务状态变化监听器,规定需要实现stateChanged(service)方法。
LoggingStateChangeListener: 服务状态变化时记录日志的类。
剩下的三个类就比较重要了,下面单独分析Service、AbstractService、CompositeService。
关键类分析
1. Service
这是一个接口类,规定了要实现的方法。
初始化/开始/停止/关闭方法,在指定方法中,必须实现对应的状态转移:
void init(Configuration config);
void start();
void stop();
void close() throws IOException;
注册/取消监听器:
void registerServiceListener(ServiceStateChangeListener listener);
void unregisterServiceListener(ServiceStateChangeListener listener);
Getter方法,获取对应属性信息:
String getName(); # 服务名称
Configuration getConfig(); # 配置对象
STATE getServiceState(); # 服务状态
long getStartTime(); # 启动事件
Throwable getFailureCause(); # 失败原因
STATE getFailureState(); # 失败时状态
判断状态:
boolean isInState(STATE state);
停止状态的阻塞通知机制:
boolean waitForServiceToStop(long timeout);
获取生命周期的历史信息:
public List<LifecycleEvent> getLifecycleHistory();
获取因期望停止服务而被阻塞的对象信息:
public Map<String, String> getBlockers();
2. AbstractService
这是一个抽象类,忠实的实现了接口类中定义的所有方法,并且在实现中用到了前面介绍的基本上所有工具类。其实现都不难理解。重点介绍状态转移的方法簇,以init为例。
public void init(Configuration conf) {
if (conf == null) {
throw new ServiceStateException("Cannot initialize service "
+ getName() + ": null configuration");
}
if (isInState(STATE.INITED)) { # 判断是否已经处于目标状态
return;
}
synchronized (stateChangeLock) { # 改变状态前,先加锁
if (enterState(STATE.INITED) != STATE.INITED) {
setConfig(conf); # 设定配置
try {
serviceInit(config); # 内部真正init方法,供子类实现
if (isInState(STATE.INITED)) {
//if the service ended up here during init,
//notify the listeners
notifyListeners(); # 状态改变后,通知监听器
}
} catch (Exception e) {
noteFailure(e); # 记录失败原因
ServiceOperations.stopQuietly(LOG, this); # 如果失败,则停止
throw ServiceStateException.convert(e);
}
}
}
}
可以看到,这类方法的思路是,指定基本流程,并提供serviceXXX以供子类扩展。
3. CompositeService
这是AbstractService的子类,顾名思义,是一个包含其他服务的复合服务,可以类比于文件系统的目录文件和普通文件。它重写的主要方法簇为serviceXXX,以serviceInit为例:
protected void serviceInit(Configuration conf) throws Exception {
List<Service> services = getServices(); # 获取服务列表
if (LOG.isDebugEnabled()) {
LOG.debug(getName() + ": initing services, size=" + services.size());
}
for (Service service : services) { # 对包含的各个服务初始化
service.init(conf);
}
super.serviceInit(conf); # 超类的初始化
}
除此之外,还有管理子服务的若干方法,如addService、addIfService、removeService等,以及容纳子服务的链表:
private final List<Service> serviceList = new ArrayList<Service>();
总结
以上就是所有关于Service模块的东西,总体比较简单,在我们之后探索各个功能模块时会频繁遇到。熟悉其中的方法即可,实现逻辑并不复杂。