Hadoop2原始碼分析-YARN 的服務庫和事件庫

哥不是小蘿莉發表於2015-07-23

1.概述

  在《Hadoop2原始碼分析-YARN RPC 示例介紹》一文當中,給大家介紹了YARN 的 RPC 機制,以及相關程式碼的演示,今天我們繼續去學習 YARN 的服務庫和事件庫,分享目錄如下所示:

  • 服務庫和事件庫介紹
  • 使用示例
  • 截圖預覽

  下面開始今天的內容分享。

2.服務庫和事件庫介紹

2.1服務庫

  YARN對於生命週期較長的物件使用服務的物件模型進行管理,主要特點如下:

  • 用於被服務化的物件包含4個狀態,他們分別是:被建立、已初始化、已啟動和已停止。原始碼地址在 org.apache.hadoop.service 的 Service 介面中,內容如下所示:
public enum STATE {
    /** Constructed but not initialized */
    NOTINITED(0, "NOTINITED"),

    /** Initialized but not started or stopped */
    INITED(1, "INITED"),

    /** started and not stopped */
    STARTED(2, "STARTED"),

    /** stopped. No further state transitions are permitted */
    STOPPED(3, "STOPPED");

    /**
     * An integer value for use in array lookup and JMX interfaces.
     * Although {@link Enum#ordinal()} could do this, explicitly
     * identify the numbers gives more stability guarantees over time.
     */
    private final int value;

    /**
     * A name of the state that can be used in messages
     */
    private final String statename;

    private STATE(int value, String name) {
      this.value = value;
      this.statename = name;
    }

    /**
     * Get the integer value of a state
     * @return the numeric value of the state
     */
    public int getValue() {
      return value;
    }

    /**
     * Get the name of a state
     * @return the state's name
     */
    @Override
    public String toString() {
      return statename;
    }
  }
public abstract class AbstractService implements Service {
  
  // ......

}

  通過閱讀程式碼,我們可以看出,服務的物件它實現了介面Service,並定義了最基本的服務狀態:建立、初始化、啟動以及停止。對於 AbstractService 類來說,它實現了 Service 介面。

  • 任何服務狀態的變化都可以觸發其他的動作,例如:
public void start() {
    if (isInState(STATE.STARTED)) {
      return;
    }
    //enter the started state
    synchronized (stateChangeLock) {
      if (stateModel.enterState(STATE.STARTED) != STATE.STARTED) {
        try {
          startTime = System.currentTimeMillis();
          serviceStart();
          if (isInState(STATE.STARTED)) {
            //if the service started (and isn't now in a later state), notify
            if (LOG.isDebugEnabled()) {
              LOG.debug("Service " + getName() + " is started");
            }
            notifyListeners();
          }
        } catch (Exception e) {
          noteFailure(e);
          ServiceOperations.stopQuietly(LOG, this);
          throw ServiceStateException.convert(e);
        }
      }
    }
  }

  這裡,我們會去觸發一個監聽動作,全域性監聽狀態的改變,異常的捕捉監聽等。

  • 可以通過組合的方式進行服務組合,這樣做的好處是便於統一去管理:在 YARN 中,如果是非組合服務,可以直接繼承 AbstractService 類,否則需繼承 CompositeService。

2.2事件庫

  在 YARN 中,核心服務其本質就是一箇中央非同步排程器,包含有ResourceManager、 NodeManager、MRAppMaster等內容,YARN 事件與事件處理器的關係在 

org.apache.hadoop.yarn.event  中。在使用 YARN 事件庫的時候,需要先定義一箇中央非同步排程器 AsyncDispatcher,它負責事件的處理與轉發,然後我們根據實際業務需求定義一系列事件 Event 與事件處理器 EventHandler,並將事件註冊到中央非同步排程器中用於完成事件統一管理和應用排程。流程如下圖所示:

3.使用示例

  接下來,我們編寫示例程式碼,去程式碼中理解這部分流程。

  • 首先是 JMRAppMaster 類:
package cn.hadoop.task.exec;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.service.Service;
import org.apache.hadoop.yarn.event.AsyncDispatcher;
import org.apache.hadoop.yarn.event.Dispatcher;
import org.apache.hadoop.yarn.event.EventHandler;

import cn.hadoop.task.CompositeService;
import cn.hadoop.task.JobEvent;
import cn.hadoop.task.JobEventType;
import cn.hadoop.task.TaskEvent;
import cn.hadoop.task.TaskEventType;

/**
 * @Date Jul 22, 2015
 *
 * @Author dengjie
 *
 * @Note TODO
 */
public class JMRAppMaster extends CompositeService {
    private Dispatcher dispatcher; // AsyncDispatcher
    private String jobID;
    private int taskNumber; // include numbers
    private String[] taskIDs; // include all task

    public JMRAppMaster(String name, String jobID, int taskNumber) {
        super(name);
        this.jobID = jobID;
        this.taskNumber = taskNumber;
        taskIDs = new String[taskNumber];
        for (int i = 0; i < taskNumber; i++) {
            taskIDs[i] = new String(this.jobID + "_task_" + i);
        }
    }

    public void serviceInit(Configuration conf) throws Exception {
        dispatcher = new AsyncDispatcher();// default a AsyncDispatcher
        dispatcher.register(JobEventType.class, new JobEventDispatcher());// register a job
        dispatcher.register(TaskEventType.class, new TaskEventDispatcher());// register a task
        addService((Service) dispatcher);
        super.serviceInit(conf);
    }

    public Dispatcher getDispatcher() {
        return dispatcher;
    }

    private class JobEventDispatcher implements EventHandler<JobEvent> {

        @SuppressWarnings("unchecked")
        public void handle(JobEvent event) {
            if (event.getType() == JobEventType.JOB_KILL) {
                System.out.println("Receive JOB_KILL event, killing all the tasks");
                for (int i = 0; i < taskNumber; i++) {
                    dispatcher.getEventHandler().handle(new TaskEvent(taskIDs[i], TaskEventType.T_KILL));
                }
            } else if (event.getType() == JobEventType.JOB_INIT) {
                System.out.println("Receive JOB_INIT event, scheduling tasks");
                for (int i = 0; i < taskNumber; i++) {
                    dispatcher.getEventHandler().handle(new TaskEvent(taskIDs[i], TaskEventType.T_SCHEDULE));
                }
            }
        }
    }

    private class TaskEventDispatcher implements EventHandler<TaskEvent> {

        public void handle(TaskEvent event) {
            if (event.getType() == TaskEventType.T_KILL) {
                System.out.println("Receive T_KILL event of task id " + event.getTaskID());
            } else if (event.getType() == TaskEventType.T_SCHEDULE) {
                System.out.println("Receive T_SCHEDULE event of task id " + event.getTaskID());
            }
        }
    }
}

  另外,還需要新增一些其他類,這些類以來可以在 Hadoop 原始碼工程中找到,這裡就不貼程式碼了,大家可以到 Hadoop 工程的原始碼中找到對應的類,相關類名如下圖所示:

  接下來是一個測試類,去測試一下我們所編寫的示例流程。

  • JMRAppMasterTest類:

package cn.hadoop.rpc.test.yarn.task;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.yarn.conf.YarnConfiguration;

import cn.hadoop.task.JobEvent;
import cn.hadoop.task.JobEventType;
import cn.hadoop.task.exec.JMRAppMaster;

/**
 * @Date Jul 22, 2015
 *
 * @Author dengjie
 *
 * @Note TODO
 */
public class JMRAppMasterTest {
    @SuppressWarnings({ "unchecked", "resource" })
    public static void main(String[] args) {
        String jobID = "job_20150723_11";
        JMRAppMaster appMaster = new JMRAppMaster("Simple MRAppMaster Test", jobID, 10);
        YarnConfiguration conf = new YarnConfiguration(new Configuration());
        try {
            appMaster.serviceInit(conf);
            appMaster.serviceStart();
        } catch (Exception e) {
            e.printStackTrace();
        }
        appMaster.getDispatcher().getEventHandler().handle(new JobEvent(jobID, JobEventType.JOB_KILL));
        appMaster.getDispatcher().getEventHandler().handle(new JobEvent(jobID, JobEventType.JOB_INIT));
    }
}

4.截圖預覽

  在編寫完成相關流程程式碼後,我們執行程式碼來觀察整個流程,截圖如下所示:

5.總結

  在編寫這部分流程程式碼時,可以參考 Hadoop YARN 部分的工程原始碼,通過執行除錯程式碼,掌握對事件庫和服務庫的流程,以及它們的工作機制。另外,在編寫的過程當中,最好將原始碼的檔案引入到自己的工程,不要單獨使用 JAR 包的方式匯入,由於我們是獨立執行某個模組,需要改動原始碼的函式訪問許可權,若是直接引入 JAR 包地址,會導致函式修飾許可權問題而不能執行,這裡大家在執行除錯的時候注意即可。

6.結束語

  這篇部落格就和大家分享到這裡,如果大家在研究學習的過程當中有什麼問題,可以加群進行討論或傳送郵件給我,我會盡我所能為您解答,與君共勉!

相關文章