當我們定義一個Counter時,我們首先要定義一枚舉類型:
public static enum MY_COUNTER{
CORRUPTED_DATA_COUNTER,
NORMAL_DATA_COUNTER
};
然後,我們就可以在mapper或reducer裡面增加它的值:
context.getCounter(MY_COUNTER.CORRUPTED_DATA_COUNTER).increment(1);
我們在第(一)篇講InputFormat時,我們有看到Mapper.class中的Context類是繼承與MapContext類的,而MapContext又繼承與TaskInputOutputContext,我們可以從TaskInputOutputContext的getCounter()方法看見,這個方法實際上是調用了StatusReporter的getCounter()方法,StatusReporter在後面談到。
接着,我們在送出job,waitForCompletion()方法等待job執行完後,就可以通過
Counters counters = job.getCounters();
Counter counter = counters.findCounter(MYCOUNTER.CORRUPTED_DATA_COUNTER);
System.out.println(counter.getValue());
這樣就将我們想要輸出的計數器輸出來。
Counter對應我們寫的enum類型中的一個枚舉常量,比如MY_COUNTER.CORRUTED_DATA_COUNTER,它由name,displayName和value表示,value是Counter目前計數值。Counter、CounterGroup和Counters都實作了Writable接口,由于Counter是全局的,是以它們的讀寫方法都是synchronized方法,以保證線程安全。
CounterGroup對應我們寫的enum類型,比如MY_COUNTER。CounterGroup有name,displayName,TreeMap類型的counters,以及一個ResourceBoundle bundle。counters存放的是enum裡面的所有枚舉常量對應的Counter。而bundle是用來本地化Counter的名字的。舉個例子:src\mapred\org\apache\hadoop\mapred下有一個JobInProgress_Counter.properties檔案,内容是這樣的:
# ResourceBundle properties file for job-level counters
CounterGroupName= Job Counters
NUM_FAILED_MAPS.name= Failed map tasks
NUM_FAILED_REDUCES.name= Failed reduce tasks
TOTAL_LAUNCHED_MAPS.name= Launched map tasks
TOTAL_LAUNCHED_REDUCES.name= Launched reduce tasks
OTHER_LOCAL_MAPS.name= Other local map tasks
DATA_LOCAL_MAPS.name= Data-local map tasks
RACK_LOCAL_MAPS.name= Rack-local map tasks
FALLOW_SLOTS_MILLIS_MAPS.name= Total time spent by all maps waiting after reserving slots (ms)
FALLOW_SLOTS_MILLIS_REDUCES.name= Total time spent by all reduces waiting after reserving slots (ms)
它存放的是job級别的counters的本地化名字。形式是name = displayName。
這樣,我們就可以每次利用name從改檔案中讀取displayName,使得當我們改變這個properties檔案中的某個displayName的時候,不需要改動程式。