Hadoop2.6.0的事件分類與實現

前言

說實在的,在閱讀Hadoop YARN的源碼以前,我對於Java枚舉的使用相形見絀。YARN中實現的事件在可讀性、可維護性、可擴展性方面的工做都值得借鑑。java

概念

在具體分析源碼以前,咱們先看看YARN是如何定義一個事件的。好比做業啓動的事件,不少人可能會用常量將它定義到一個class文件中,就像下面這樣:api

 

[java] view plain copyide

class Constants {  
  public static final String JOB_START_EVENT = "jobStart";  
}  

或者簡單的使用枚舉,就像下面這樣;oop

[java] view plain copythis

enum Enums {  
  JOB_START_EVENT("jobStart");  
  private String name;  
  private Enums(String name) {  
    this.name = name;  
  }  
}  

以後,當增長了做業中止的事件,代碼會變爲:spa

[java] view plain copy.net

class Constants {  
  public static final String JOB_START_EVENT = "jobStart";  
  public static final String JOB_END_EVENT = "jobEnd";  
}  

或者:code

[java] view plain copyblog

enum Enums {  
  JOB_START_EVENT("jobStart"),  
  JOB_END_EVENT("jobEnd");  
  private String name;  
  private Enums(String name) {  
    this.name = name;  
  }  
}  

咱們的系統每每很複雜,這時候引入了任務的概念,包括任務啓動、任務中止的事件。隨着業務發展,有更多的概念被加進來,就像下面這樣;接口

[java] view plain copy

class Constants {  
  public static final String JOB_START_EVENT = "jobStart";  
  public static final String JOB_END_EVENT = "jobEnd";  
  public static final String TASK_START_EVENT = "taskStart";  
  public static final String TASK_END_EVENT = "taskEnd";  
  // 其它各類概念的常量  
}  

或者:

[java] view plain copy

enum Enums {  
  JOB_START_EVENT("jobStart"),  
  JOB_END_EVENT("jobEnd"),  
  // 其它各類概念的常量枚舉  
  TASK_START_EVENT("taskStart"),  
  TASK_END_EVENT("taskEnd");  
  private String name;  
  private Enums(String name) {  
    this.name = name;  
  }  
}  

當加入的常量值愈來愈多時,你會發現以上使用方式愈來愈不可維護。各類概念混雜在一塊兒,顯得雜亂無章。你可能會說,我不會這麼傻,我會將做業與任務以及其它概念的常量值分而治之,每一個業務概念相關的放入一個文件,就像下面這樣:

[java] view plain copy

  1. class JobConstants {  
      public static final String JOB_START_EVENT = "jobStart";  
      public static final String JOB_END_EVENT = "jobEnd";  
    }  

     

[java] view plain copy

class TaskConstants {  
  public static final String TASK_START_EVENT = "taskStart";  
  public static final String TASK_END_EVENT = "taskEnd";  
}  


或者:

[java] view plain copy

enum JobEnums {  
  JOB_START_EVENT("jobStart"),  
  JOB_END_EVENT("jobEnd");  
  private String name;  
  private JobEnums (String name) {  
    this.name = name;  
  }  
}  

 

[java] view plain copy

  1. enum TaskEnums {  
      TASK_START_EVENT("taskStart"),  
      TASK_END_EVENT("taskEnd");  
      private String name;  
      private TaskEnums (String name) {  
        this.name = name;  
      }  
    }  

     

如今業務出現了新的變化,每種枚舉值除了name屬性以外,還增長了code屬性。假如你以前選擇了常量值來實現,此時不可避免的須要重構。若是你選擇了枚舉,說明你初步的選擇是明智的,你能夠這樣來擴展:

 

 

[java] view plain copy

  1. enum JobEnums {  
      JOB_START_EVENT(10, "jobStart"),  
      JOB_END_EVENT(20, "jobEnd");  
      private int code;  
      private String name;  
      private JobEnums (int code, String name) {  
        this.code = code;    
        this.name = name;  
      }  
    }  

     

 

[java] view plain copy

  1. enum TaskEnums {  
      TASK_START_EVENT(110, "taskStart"),  
      TASK_END_EVENT(120, "taskEnd");  
      private int code;  
      private String name;  
      private TaskEnums (int code, String name)   {  
        this.code = code;  
        this.name = name;  
      }  
    }  

     

可悲的是,你不得不在每個枚舉中都重複加入相似的代碼。也許你認爲這只不過是增長些許的工做量,你操做鍵盤的手法熟練而迷人,幾回快速的複製操做就能夠完成。噩夢遠沒有結束,新的需求給兩個枚舉類型融入了新的不一樣——JobEnums增長了description屬性,而TaskEnums則增長了timestamp字段。此外,二者還必須都增長hashCode方法以用於散列。增長這些功能後,代碼將變爲:

 

 

[java] view plain copy

enum JobEnums {  
  JOB_START_EVENT(10, "jobStart", "job start description"),  
  JOB_END_EVENT(20, "jobEnd", "job end description");  
  private int code;  
  private String name;  
  private String description;  
  private JobEnums (int code, String name, String description) {  
    this.code = code;    
    this.name = name;  
    this.description = description;  
  }  
  
  public int hashCode() {  
    return this.name.hashCode() + this.description.hashCode();  
  }  
}  

[java] view plain copy

enum TaskEnums {  
  TASK_START_EVENT(110, "taskStart", 1460977775087),  
  TASK_END_EVENT(120, "taskEnd", 1460977775088);  
  private int code;  
  private String name;  
  private long timestamp;  
  private TaskEnums (int code, String name, long timestamp)   {  
    this.code = code;  
    this.name = name;  
    this.timestamp = timestamp;  
  }  
  
  public int hashCode() {  
    return this.name.hashCode();  
  }  
}  

隨着業務的發展,你會發現你須要維護的枚舉類型差別愈來愈多。即使它們之間有所不一樣,但是卻有不少內容是重複的。爲了解決枚舉與常量在可讀性、可維護性、可複用性、可擴展性等方面的問題,Hadoop將事件進行了如下定義:

 

事件 = 事件名稱 + 事件類型

好比做業啓動事件 = 做業事件 + 做業事件類型

事件與事件類型

Hadoop2.6.0中的事件多種多樣,最爲常見的包括:ContainerEvent、ApplicationEvent、JobEvent、RMAppEvent、RMAppAttemptEvent、TaskEvent、TaskAttemptEvent等。爲了解決枚舉與常量在可讀性、可維護性、可複用性、可擴展性等方面的問題,Hadoop對事件進行了如下抽象:

 

[java] view plain copy

/** 
 * Interface defining events api. 
 * 
 */  
@Public  
@Evolving  
public interface Event<TYPE extends Enum<TYPE>> {  
  
  TYPE getType();  
  long getTimestamp();  
  String toString();  
}  

 

以上接口說明了任何一個具體事件都是一個枚舉類型,並且有一個事件類型屬性(用泛型標記TYPE表示),一個時間戳及toString()方法。

全部事件都有一個基本實現AbstractEvent,其實現以下:

 

[java] view plain copy

/** 
 * Parent class of all the events. All events extend this class. 
 */  
@Public  
@Evolving  
public abstract class AbstractEvent<TYPE extends Enum<TYPE>>   
    implements Event<TYPE> {  
  
  private final TYPE type;  
  private final long timestamp;  
  
  // use this if you DON'T care about the timestamp  
  public AbstractEvent(TYPE type) {  
    this.type = type;  
    // We're not generating a real timestamp here.  It's too expensive.  
    timestamp = -1L;  
  }  
  
  // use this if you care about the timestamp  
  public AbstractEvent(TYPE type, long timestamp) {  
    this.type = type;  
    this.timestamp = timestamp;  
  }  
  
  @Override  
  public long getTimestamp() {  
    return timestamp;  
  }  
  
  @Override  
  public TYPE getType() {  
    return type;  
  }  
  
  @Override  
  public String toString() {  
    return "EventType: " + getType();  
  }  
}  

以JobEvent表示做業事件,其實現以下:

[java] view plain copy

/** 
 * This class encapsulates job related events. 
 * 
 */  
public class JobEvent extends AbstractEvent<JobEventType> {  
  
  private JobId jobID;  
  
  public JobEvent(JobId jobID, JobEventType type) {  
    super(type);  
    this.jobID = jobID;  
  }  
  
  public JobId getJobId() {  
    return jobID;  
  }  
  
}  

TaskEvent表示任務事件,其實現以下:

 

 

[java] view plain copy

/** 
 * this class encapsulates task related events. 
 * 
 */  
public class TaskEvent extends AbstractEvent<TaskEventType> {  
  
  private TaskId taskID;  
  
  public TaskEvent(TaskId taskID, TaskEventType type) {  
    super(type);  
    this.taskID = taskID;  
  }  
  
  public TaskId getTaskID() {  
    return taskID;  
  }  
}  

事件類型屬性(用泛型標記TYPE表示)在任務事件中對應的是TaskEventType,其實現以下:

 

[java] view plain copy

/** 
 * Event types handled by Task. 
 */  
public enum TaskEventType {  
  
  //Producer:Client, Job  
  T_KILL,  
  
  //Producer:Job  
  T_SCHEDULE,  
  T_RECOVER,  
  
  //Producer:Speculator  
  T_ADD_SPEC_ATTEMPT,  
  
  //Producer:TaskAttempt  
  T_ATTEMPT_LAUNCHED,  
  T_ATTEMPT_COMMIT_PENDING,  
  T_ATTEMPT_FAILED,  
  T_ATTEMPT_SUCCEEDED,  
  T_ATTEMPT_KILLED  
}  


JobEventType相似,再也不贅述。

 

這種實現將枚舉與各類事件之間的差別(表如今屬性和方法的不一樣)解耦,極大地擴展了可讀性、可維護性,而且保留了相同邏輯的代碼複用。

相關文章
相關標籤/搜索