vlambda博客
学习文章列表

【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模块的东西,总体比较简单,在我们之后探索各个功能模块时会频繁遇到。熟悉其中的方法即可,实现逻辑并不复杂。