关于Yarn源码那些事-前传之ResourceManager篇(一)初始化

在关于Yarn那些事的博客里,介绍的主要是针对任务提交的一个动态流程说明,而其中牵涉到的一些细节问题,必须通过Resourcemanager的启动和NodeManager的启动,来更好的说明。

而本系列,就详细说说ResourceManager启动过程中,都发生了什么。

我们都知道,Yarn的启动脚本是start-yan.sh,我们就从这个脚本开始,琢磨琢磨。

[java] view plain  copy
 
  1. "$bin"/yarn-daemon.sh --config $YARN_CONF_DIR  start resourcemanager  

脚本里这句话,指向了本目录下的yarn-daemon.sh脚本,命令参数指定了resourcemanager,接着看yarn-daemon.sh脚本:

[java] view plain  copy
 
  1. nohup nice -n $YARN_NICENESS "$HADOOP_YARN_HOME"/bin/yarn --config $YARN_CONF_DIR $command "$@" > "$log" 2>&1 < /dev/null &  

这句话很关键,交代了我们实际的启动脚本是bin目录下的yarn,我们看下:

[java] view plain  copy
 
  1. elif [ "$COMMAND" = "resourcemanager" ] ; then  
  2.   CLASSPATH=${CLASSPATH}:$YARN_CONF_DIR/rm-config/log4j.properties  
  3.   CLASSPATH=${CLASSPATH}:"$HADOOP_YARN_HOME/$YARN_DIR/timelineservice/*"  
  4.   CLASSPATH=${CLASSPATH}:"$HADOOP_YARN_HOME/$YARN_DIR/timelineservice/lib/*"  
  5.   CLASS='org.apache.hadoop.yarn.server.resourcemanager.ResourceManager'  
  6.   YARN_OPTS="$YARN_OPTS $YARN_RESOURCEMANAGER_OPTS"  
  7.   if [ "$YARN_RESOURCEMANAGER_HEAPSIZE" != "" ]; then  
  8.     JAVA_HEAP_MAX="-Xmx""$YARN_RESOURCEMANAGER_HEAPSIZE""m"  
  9.   fi  

终于顺利找到了根基,原来根据我们指定的脚本,找到的是ResourceManager这个类来启动的,下面就看看这个类。

先来看下注释:

[java] view plain  copy
 
  1. /** 
  2.  * The ResourceManager is the main class that is a set of components. "I am the 
  3.  * ResourceManager. All your resources belong to us..." 
  4.  * 
  5.  */  
  6. @SuppressWarnings("unchecked")  
  7. public class ResourceManager extends CompositeService implements Recoverable  

格外霸气,管理整个集群内所有的资源,并且继承了CompositeService类,这是一个服务类,不多介绍了,主要提供了一些服务初始化和启动的方法,供子类使用。

从ResourceManager的成员变量开始看起:

[java] view plain  copy
 
  1. protected ClientToAMTokenSecretManagerInRM clientToAMSecretManager = new ClientToAMTokenSecretManagerInRM();  
  2.     protected RMContainerTokenSecretManager containerTokenSecretManager;  
  3.     protected NMTokenSecretManagerInRM nmTokenSecretManager;  
  4.   
  5.     protected AMRMTokenSecretManager amRmTokenSecretManager;  
  6.   
  7.     private Dispatcher rmDispatcher;  
  8.   
  9.     protected ResourceScheduler scheduler;  
  10.     private ClientRMService clientRM;  
  11.     protected ApplicationMasterService masterService;  
  12.     private ApplicationMasterLauncher applicationMasterLauncher;  
  13.     private AdminService adminService;  
  14.     private ContainerAllocationExpirer containerAllocationExpirer;  
  15.     protected NMLivelinessMonitor nmLivelinessMonitor;  
  16.     protected NodesListManager nodesListManager;  
  17.     private EventHandler<SchedulerEvent> schedulerDispatcher;  
  18.     protected RMAppManager rmAppManager;  
  19.     protected ApplicationACLsManager applicationACLsManager;  
  20.     protected QueueACLsManager queueACLsManager;  
  21.     protected RMDelegationTokenSecretManager rmDTSecretManager;  
  22.     private DelegationTokenRenewer delegationTokenRenewer;  
  23.     private WebApp webApp;  
  24.     protected RMContext rmContext;  
  25.     protected ResourceTrackerService resourceTracker;  
  26.     private boolean recoveryEnabled;  

很多,具体可以参照每个类的用法,在此不多说,其中牵涉到Application较多的,比如RMAppManager,RMContext等,需要细看,这是废话,每个都值得研究。

看Main方法:

[java] view plain  copy
 
  1. Configuration conf = new YarnConfiguration();  
  2. ResourceManager resourceManager = new ResourceManager();  
  3. ShutdownHookManager.get().addShutdownHook(new CompositeServiceShutdownHook(resourceManager),  
  4.         SHUTDOWN_HOOK_PRIORITY);  
  5. setHttpPolicy(conf);  
  6. resourceManager.init(conf);  
  7. resourceManager.start();  

直接把目光聚焦在这里,我们重点研究下服务的初始化和启动。

[java] view plain  copy
 
  1. this.rmDispatcher = createDispatcher();  
  2.         addIfService(this.rmDispatcher);  

初始化的第一个关键点,创建调度器,这是ResourceManager异步调度的关键,看看这个方法:很简单:

[java] view plain  copy
 
  1. protected Dispatcher createDispatcher() {  
  2.         return new AsyncDispatcher();  
  3.     }  

很明显,这是个异步调度器,看看这个类的注释和初始化步骤:

[java] view plain  copy
 
  1. /** 
  2.  * Dispatches {@link Event}s in a separate thread. Currently only single thread 
  3.  * does that. Potentially there could be multiple channels for each event type 
  4.  * class and a thread pool can be used to dispatch the events. 
  5.  */  
  6. @SuppressWarnings("rawtypes")  
  7. @Public  
  8. @Evolving  
  9. public class AsyncDispatcher extends AbstractService implements Dispatcher   

这也是一个需要启动的服务,用于事件的调度:

[java] view plain  copy
 
  1. public AsyncDispatcher() {  
  2.         this(new LinkedBlockingQueue<Event>());  
  3.     }  
  4.   
  5.     public AsyncDispatcher(BlockingQueue<Event> eventQueue) {  
  6.         super("Dispatcher");  
  7.         this.eventQueue = eventQueue;  
  8.         this.eventDispatchers = new HashMap<Class<? extends Enum>, EventHandler>();  
  9.     }  

注意,Dispatcher内部封装了一个阻塞队列,运行过程中会把事件都放在这个池子里,并进行调度处理,同时定义了一个eventDispatchers,后续代码更容易看懂这个map的作用:

我们仔细看看AsyncDispatcher的服务初始化代码:

[java] view plain  copy
 
  1. @Override  
  2.     protected void serviceInit(Configuration conf) throws Exception {  
  3.         this.exitOnDispatchException = conf.getBoolean(Dispatcher.DISPATCHER_EXIT_ON_ERROR_KEY,  
  4.                 Dispatcher.DEFAULT_DISPATCHER_EXIT_ON_ERROR);  
  5.         super.serviceInit(conf);  
  6.     }  

目前来说,AsyncDispatcher的初始化代码先到这儿,我们继续看ResourceManager的服务初始化代码:

[java] view plain  copy
 
  1. this.amRmTokenSecretManager = createAMRMTokenSecretManager(conf);  

我们看到内部有个这个成员变量:

[java] view plain  copy
 
  1. /** 
  2.  * AMRM-tokens are per ApplicationAttempt. If users redistribute their 
  3.  * tokens, it is their headache, god save them. I mean you are not supposed to 
  4.  * distribute keys to your vault, right? Anyways, ResourceManager saves each 
  5.  * token locally in memory till application finishes and to a store for restart, 
  6.  * so no need to remember master-keys even after rolling them. 
  7.  */  
  8. public class AMRMTokenSecretManager extends  
  9.     SecretManager<AMRMTokenIdentifier>   

看注释,清晰明了,每次提交一个ApplicationAttempt时候,都不用再递交自己的token了,实际上还是一个身份验证工具(自己的理解)。

[java] view plain  copy
 
  1. this.containerAllocationExpirer = new ContainerAllocationExpirer(this.rmDispatcher);  
  2.         addService(this.containerAllocationExpirer);  

看下这两句话ContainerAllocationExpirer,并且加到了serviceList中,用于最后的初始化,我们看看这个是什么作用,注释非常简单,还是留在动态提交ApplicationMaster的时候分析吧,其实主要是用来判断分配的container是否在规定时间内得到启动的:

[java] view plain  copy
 
  1. AMLivelinessMonitor amLivelinessMonitor = createAMLivelinessMonitor();  
  2.         addService(amLivelinessMonitor);  

看这个AMLiveLinessMonitor,顾名思义,是用来检查ApplicationLivenessMonitor是否存活的,其继承了这个类:

[java] view plain  copy
 
  1. /** 
  2.  * A simple liveliness monitor with which clients can register, trust the 
  3.  * component to monitor liveliness, get a call-back on expiry and then finally 
  4.  * unregister. 
  5.  */  
  6. @Public  
  7. @Evolving  
  8. public abstract class AbstractLivelinessMonitor<O> extends AbstractService   

同时,ContainerAllocationMonitor也继承了这个类,就是用于让客户端监控的:

[java] view plain  copy
 
  1. AMLivelinessMonitor amFinishingMonitor = createAMLivelinessMonitor();  
  2.         addService(amFinishingMonitor);  

下面初始化了一个一样的AMLiveLinessMonitor,但是变量名不同,同样是起监控作用的:

接下来看RMStateStore的初始化:

[java] view plain  copy
 
  1. boolean isRecoveryEnabled = conf.getBoolean(YarnConfiguration.RECOVERY_ENABLED,  
  2.                 YarnConfiguration.DEFAULT_RM_RECOVERY_ENABLED);  
  3.   
  4.         RMStateStore rmStore = null;  
  5.         if (isRecoveryEnabled) {  
  6.             recoveryEnabled = true;  
  7.             rmStore = RMStateStoreFactory.getStore(conf);  
  8.         } else {  
  9.             recoveryEnabled = false;  
  10.             rmStore = new NullRMStateStore();  
  11.         }  

对于大部分成员变量的初始化不予多说,先看下RMStateStore的初始化,在我们默认配置下:isRecoveryEnabled为false,所以创建了一个空的RMStateStore,即NullRMStateStore,对于ResourceManager的状态进行存储:

[java] view plain  copy
 
  1. rmStore.init(conf);  
  2. rmStore.setRMDispatcher(rmDispatcher);  

这里面注意下,rmStore内部的dispatcher与RM的dispatcher不是同一个,代码如下:

[java] view plain  copy
 
  1. private Dispatcher rmDispatcher;  
  2. AsyncDispatcher dispatcher;  

RMStateStore内部有两个调度器,rmDispatcher是RM的调度器,而dispatcher则是其内部用来调度事件的调度器,对于RMStateStore的init代码有些绕,仔细看下:

[java] view plain  copy
 
  1. @Override  
  2.     public void init(Configuration conf) {  
  3.         if (conf == null) {  
  4.             throw new ServiceStateException("Cannot initialize service " + getName() + ": null configuration");  
  5.         }  
  6.         if (isInState(STATE.INITED)) {  
  7.             return;  
  8.         }  
  9.         synchronized (stateChangeLock) {  
  10.             if (enterState(STATE.INITED) != STATE.INITED) {  
  11.                 setConfig(conf);  
  12.                 try {  
  13.                     serviceInit(config);  
  14.                     if (isInState(STATE.INITED)) {  
  15.                         // if the service ended up here during init,  
  16.                         // notify the listeners  
  17.                         notifyListeners();  
  18.                     }  
  19.                 } catch (Exception e) {  
  20.                     noteFailure(e);  
  21.                     ServiceOperations.stopQuietly(LOG, this);  
  22.                     throw ServiceStateException.convert(e);  
  23.                 }  
  24.             }  
  25.         }  
  26.     }  

其实际调用的是AbstractService的init方法,其中调用到了serviceInit方法,而这个方法,则是RMStateStore的方法:

[java] view plain  copy
 
  1. public synchronized void serviceInit(Configuration conf) throws Exception {  
  2.         // create async handler  
  3.         dispatcher = new AsyncDispatcher();  
  4.         dispatcher.init(conf);  
  5.         dispatcher.register(RMStateStoreEventType.class, new ForwardingEventHandler());  
  6.         initInternal(conf);  
  7.     }  

很清楚看到了内部封装了一个自己的dispatcher,用于调度RMStateStoreEventType类型的事件:

下面接着看:

[java] view plain  copy
 
  1. this.rmContext = new RMContextImpl(this.rmDispatcher, rmStore, this.containerAllocationExpirer,  
  2.                 amLivelinessMonitor, amFinishingMonitor, delegationTokenRenewer, this.amRmTokenSecretManager,  
  3.                 this.containerTokenSecretManager, this.nmTokenSecretManager, this.clientToAMSecretManager);  

这是重头戏,我们必须看看这个拥有如此多成员变量的RMContextImpl到底是什么:

[java] view plain  copy
 
  1. /** 
  2.  * Context of the ResourceManager. 
  3.  */  
  4. public interface RMContext {  

这是RMContextImpl父类的注释,是ResourceManager的上下文,就相当于管家了,基本大权在握,是ResourceManager的心腹。

接下来是这儿:

[java] view plain  copy
 
  1. this.nodesListManager = new NodesListManager(this.rmContext);  

其实就相当于告诉了RM,这里到底有多少个子节点可供使用,而且给新建的NodesListManager内部也安插了RM的心腹,即RMContextImpl。

[java] view plain  copy
 
  1. this.rmDispatcher.register(NodesListManagerEventType.class, this.nodesListManager);  

看到这儿,我们又得回去看AsyncDispatcher中的一个register方法:

[java] view plain  copy
 
  1. @SuppressWarnings("unchecked")  
  2.     @Override  
  3.     public void register(Class<? extends Enum> eventType, EventHandler handler) {  
  4.         /* check to see if we have a listener registered */  
  5.         EventHandler<Event> registeredHandler = (EventHandler<Event>) eventDispatchers.get(eventType);  
  6.         LOG.info("Registering " + eventType + " for " + handler.getClass());  
  7.         if (registeredHandler == null) {  
  8.             eventDispatchers.put(eventType, handler);  
  9.         } else if (!(registeredHandler instanceof MultiListenerHandler)) {  
  10.             /* for multiple listeners of an event add the multiple listener handler */  
  11.             MultiListenerHandler multiHandler = new MultiListenerHandler();  
  12.             multiHandler.addHandler(registeredHandler);  
  13.             multiHandler.addHandler(handler);  
  14.             eventDispatchers.put(eventType, multiHandler);  
  15.         } else {  
  16.             /* already a multilistener, just add to it */  
  17.             MultiListenerHandler multiHandler = (MultiListenerHandler) registeredHandler;  
  18.             multiHandler.addHandler(handler);  
  19.         }  
  20.     }  

仔细看来,其实就相当于eventDispatcher的充实,把各类事件即相应的处理,都送给eventdispatcher,方便后续出现类似事件,eventdispatcher能够迅速找到对应的对象来进行处理。

这里,就相当于把对应于NodeManager出现的事情,都交给了NodeListManager,这就是你的工作了。

[java] view plain  copy
 
  1. public enum NodesListManagerEventType {  
  2.     NODE_USABLE, NODE_UNUSABLE  
  3. }  

如果出现了节点可用和不可用的事情,你就得迅速予以处理了。

[java] view plain  copy
 
  1. // Initialize the scheduler  
  2.         this.scheduler = createScheduler();  
  3.         this.schedulerDispatcher = createSchedulerEventDispatcher();  
  4.         addIfService(this.schedulerDispatcher);  
  5.         this.rmDispatcher.register(SchedulerEventType.class, this.schedulerDispatcher);  

接下来,创建了一个调度器,这一段得仔细看看了,因为yarn中的调度器非常重要,我们作业的初始化,都离不开它:

[java] view plain  copy
 
  1. protected ResourceScheduler createScheduler() {  
  2.         String schedulerClassName = conf.get(YarnConfiguration.RM_SCHEDULER, YarnConfiguration.DEFAULT_RM_SCHEDULER);  
  3.         LOG.info("Using Scheduler: " + schedulerClassName);  
  4.         try {  
  5.             Class<?> schedulerClazz = Class.forName(schedulerClassName);  
  6.             if (ResourceScheduler.class.isAssignableFrom(schedulerClazz)) {  
  7.                 return (ResourceScheduler) ReflectionUtils.newInstance(schedulerClazz, this.conf);  
  8.             } else {  
  9.                 throw new YarnRuntimeException("Class: " + schedulerClassName + " not instance of "  
  10.                         + ResourceScheduler.class.getCanonicalName());  
  11.             }  
  12.         } catch (ClassNotFoundException e) {  
  13.             throw new YarnRuntimeException("Could not instantiate Scheduler: " + schedulerClassName, e);  
  14.         }  
  15.     }  

我这里的代码是hadoop 2.2.0,在默认配置下,配置的调度器是:

[java] view plain  copy
 
  1. org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler  

而接着,我们给调度器自己定义了一个调度器,换句话说,对于调度器接受的事件类型,其会继续调度给其他的handler去处理

最后,把这个调度器也注册给了RM内部的调度器:

[java] view plain  copy
 
  1. protected EventHandler<SchedulerEvent> createSchedulerEventDispatcher() {  
  2.         return new SchedulerEventDispatcher(this.scheduler);  
  3.     }  
[java] view plain  copy
 
  1. this.rmDispatcher.register(SchedulerEventType.class, this.schedulerDispatcher);  

接着,我们的异步调度器又加入了三个成分,负责对Application相关事件,ApplicationAttempt事件,RMNode事件进行调度:

[java] view plain  copy
 
  1. // Register event handler for RmAppEvents  
  2.         this.rmDispatcher.register(RMAppEventType.class, new ApplicationEventDispatcher(this.rmContext));  
  3.   
  4.         // Register event handler for RmAppAttemptEvents  
  5.         this.rmDispatcher.register(RMAppAttemptEventType.class, new ApplicationAttemptEventDispatcher(this.rmContext));  
  6.   
  7.         // Register event handler for RmNodes  
  8.         this.rmDispatcher.register(RMNodeEventType.class, new NodeEventDispatcher(this.rmContext));  

接着,我们看下这个:

[java] view plain  copy
 
  1. this.resourceTracker = createResourceTrackerService();  
  2.         addService(resourceTracker);  

从官方文档中,我们知道RM实现了对于系统全部资源的管控,而这个管控是通过RPC来实现的,NodeManager调用ResourceTracker内的方法来提交自己的资源,而RM端有相应的处理,返回命令,让NM予以执行,而ResourceTrackerSerive就是在此处初始化的:

[java] view plain  copy
 
  1. @Override  
  2.     protected void serviceInit(Configuration conf) throws Exception {  
  3.         resourceTrackerAddress = conf.getSocketAddr(YarnConfiguration.RM_RESOURCE_TRACKER_ADDRESS,  
  4.                 YarnConfiguration.DEFAULT_RM_RESOURCE_TRACKER_ADDRESS,  
  5.                 YarnConfiguration.DEFAULT_RM_RESOURCE_TRACKER_PORT);  
  6.   
  7.         RackResolver.init(conf);  
  8.         nextHeartBeatInterval = conf.getLong(YarnConfiguration.RM_NM_HEARTBEAT_INTERVAL_MS,  
  9.                 YarnConfiguration.DEFAULT_RM_NM_HEARTBEAT_INTERVAL_MS);  
  10.         if (nextHeartBeatInterval <= 0) {  
  11.             throw new YarnRuntimeException("Invalid Configuration. " + YarnConfiguration.RM_NM_HEARTBEAT_INTERVAL_MS  
  12.                     + " should be larger than 0.");  
  13.         }  
  14.   
  15.         minAllocMb = conf.getInt(YarnConfiguration.RM_SCHEDULER_MINIMUM_ALLOCATION_MB,  
  16.                 YarnConfiguration.DEFAULT_RM_SCHEDULER_MINIMUM_ALLOCATION_MB);  
  17.         minAllocVcores = conf.getInt(YarnConfiguration.RM_SCHEDULER_MINIMUM_ALLOCATION_VCORES,  
  18.                 YarnConfiguration.DEFAULT_RM_SCHEDULER_MINIMUM_ALLOCATION_VCORES);  
  19.   
  20.         super.serviceInit(conf);  
  21.     }  

可以看到,这里牵涉到了很多的默认配置的地址,所以,看代码是必要的,我们想要把配置文件全部搞通,其实认真搞通代码,就轻而易举了。

[java] view plain  copy
 
  1. masterService = createApplicationMasterService();  
  2.         addService(masterService);  

看看这一段,新建了一个ApplicationMasterService,用于对所有提交的ApplicationMaster进行管理:其中的serviceInit方法不复杂,重点在其serviceStart方法中:

下面,我们注意看下这段代码:

[java] view plain  copy
 
  1. this.rmAppManager = createRMAppManager();  
  2.         // Register event handler for RMAppManagerEvents  
  3.         this.rmDispatcher.register(RMAppManagerEventType.class, this.rmAppManager);  
  4.         this.rmDTSecretManager = createRMDelegationTokenSecretManager(this.rmContext);  
  5.         rmContext.setRMDelegationTokenSecretManager(this.rmDTSecretManager);  
  6.         clientRM = createClientRMService();  
  7.         rmContext.setClientRMService(clientRM);  
  8.         addService(clientRM);  
  9.   
  10.         adminService = createAdminService(clientRM, masterService, resourceTracker);  
  11.         addService(adminService);  
  12.   
  13.         this.applicationMasterLauncher = createAMLauncher();  
  14.         this.rmDispatcher.register(AMLauncherEventType.class, this.applicationMasterLauncher);  

这里有些东西需要注意,新建了一个ClientRMService,负责客户端与RM的所有交互,对于客户端的每个请求,我们都可以在ClientRMService下面找到相应的代码。

下面接着看,AMLauncher,这个很重要,负责ApplicationMaster的启动,必须重点分析下。

[java] view plain  copy
 
  1. this.applicationMasterLauncher = createAMLauncher();  
  2.         this.rmDispatcher.register(AMLauncherEventType.class, this.applicationMasterLauncher);  

基于RM的管家,建立了一个AMLauncher:

[java] view plain  copy
 
  1. protected ApplicationMasterLauncher createAMLauncher() {  
  2.         return new ApplicationMasterLauncher(this.rmContext);  
  3.     }  

分析下其中的serviceInit方法:

[java] view plain  copy
 
  1. @Override  
  2.     protected void serviceStart() throws Exception {  
  3.         launcherHandlingThread.start();  
  4.         super.serviceStart();  
  5.     }  

看看其中的launchHandlingThread:

[java] view plain  copy
 
  1. private class LauncherThread extends Thread {  
  2.         public LauncherThread(www.vboyule.cn) {  
  3.             super("ApplicationMaster Launcher");  
  4.         }  
  5.         @Override  
  6.         public void run() {  
  7.             while (!this.isInterrupted()) {  
  8.                 Runnable toLaunch;  
  9.                 try {  
  10.                     toLaunch = masterEvents.take();  
  11.                     launcherPool.execute(toLaunch);  
  12.                 } catch (InterruptedException e) {  
  13.                     LOG.warn(this.getClass().getName() + " interrupted. Returning.");  
  14.                     return;  
  15.                 }  
  16.             }  
  17.         }  
  18.     }  

其内部封装了一个线程池,对于调度给自身的事件不断进行处理:

在serviceInit方法的最后,调用了父类的serviceInit方法,我们看下其父类CompositeService的serviceInit方法:

[java] view plain  copy
 
  1. protected void serviceInit(Configuration conf) throws Exception {  
  2.         List<Service> services = getServices();  
  3.         if (LOG.isDebugEnabled()) {  
  4.             LOG.debug(getName(www.feifanyule.cn) + ": initing services, size=" + services.size());  
  5.         }  
  6.         for (Service service : services) {  
  7.             service.init(conf);  
  8.         }  
  9.         super.serviceInit(conf);  
  10.     }  
[java] view plain  copy
 
  1. public List<Service> www.cnzhaotai.com/ getServices() {  
  2.         synchronized (serviceList) {  
  3.             return Collections.unmodifiableList(serviceList);  
  4.         }  
  5.     }  

在看源码的时候,发现RM的serviceInit方法中,所有服务都有一个操作:

[java] view plain  copy
 
  1. protected void addService(Service service) {  
  2.         if (LOG.www.ysgj1688.com isDebugEnabled(www.vboyl130.cn)) {  
  3.             LOG.www.cnzhaotai.com/ debug("Adding service " + service.getName());  
  4.         }  
  5.         synchronized (serviceList) {  
  6.             serviceList.add(service);  
  7.         }  
  8.     }  

调用了父类中的addService方法,把所有服务添加到了serviceList中,在这里统一予以初始化,不得不说设计很精妙,在我们分析源码的时候,必须注意看下service相关的类;最顶层的父类是Serivce类,这是个接口,定义了服务的基本操作和生命周期,其唯一的实现类,是个抽象类,为AbstractService,一般来说,并不复杂的服务继承并实现AbstractService即可,复杂的服务如RM就会继承Compositeservice(AbstractService的子类)

猜你喜欢

转载自www.cnblogs.com/qwangxiao/p/9032255.html
今日推荐