日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

hadoop單元測試方法--使用和增強(qiáng)MRUnit

 風(fēng)自向前 2011-06-22

1前言

         hadoop的mapreduce提交到集群環(huán)境中出問題的定位是比較麻煩的,有時需要一遍遍的修改代碼和打出日志來排查一個很小的問題,如果數(shù)據(jù)量大的話調(diào)試起來相當(dāng)耗時間。因此有必要使用良好的單元測試手段來盡早的消除明顯的bug(當(dāng)然僅有單元測試是不夠的,畢竟跟集群的運行環(huán)境還是不一樣的)。

       然而做mapreduce的單元測試會有一些障礙,比如Map和Reduce一些參數(shù)對象是在運行時由hadoop框架傳入的,例如OutputCollector、Reporter、InputSplit等。這就需要有Mock手段。最初寫mapreduce單元測試的時候自己寫了幾個簡單的Mock也基本能滿足需要,后來發(fā)現(xiàn)MRUnit比我寫的要好用所以研究了一下就采用了。MRUnit是專門為hadoop mapreduce寫的單元測試框架,API簡潔明了,簡單實用。但也有一些薄弱的地方,比如不支持MultipleOutputs(很多情況下我們會用MultipleOutputs作為多文件輸出,后面將介紹如何增強(qiáng)MRUnit使之支持MultipleOutputs)。

2 MRUnit

         MRUnit針對不同測試對象分別使用以下幾種Driver:

l  MapDriver,針對單獨的Map測試。

l  ReduceDriver,針對單獨的Reduce測試。

l  MapReduceDriver,將Map和Reduce連貫起來測試。

l  PipelineMapReduceDriver,將多個Map-Reduce pair貫串測試。

MapDriver

         單獨測試Map的例子,假設(shè)我們要計算一個賣家的平均發(fā)貨速度。Map將搜集每一次發(fā)貨的時間間隔。針對Map的測試,

         //這是被測試的Map

    private Map mapper;

    private MapDriver<LongWritable, Text, Text, TimeInfo> mapDriver;

    @Before

    public void setUp() {

        mapper = new Map();

        mapDriver = new MapDriver<LongWritable, Text, Text, TimeInfo>();

    }

 

    @Test

    public void testMap_timeFormat2() {

        String sellerId = "444";

        //模擬輸入一行(withInput),假設(shè)從這行數(shù)據(jù)中我們可以獲得賣家(sellerId)      //某一次時間間隔 為10小時.

       //我們期望它輸出sellerId為key,value為代表1次10小時的TimeInfo對象。          //(withOutput)

       //如果輸入數(shù)據(jù)經(jīng)過Map計算后為期望的結(jié)果,那么測試通過。

       Text mapInputValue = new Text("……");

        mapDriver.withMapper(mapper)

              .withInput(null, mapInputValue)

              .withOutput(new Text(sellerId), new TimeInfo(1, 10))

              .runTest();

    }

ReduceDriver

         針對Reduce的單獨測試,還是這個例子。Reduce為根據(jù)Map或Combiner輸出的n次時間間隔的總和來計算平均時間。

    private Reduce reducer;

    @Before

    public void setUp() {

        reducer = new Reduce();

        reduceDriver = new ReduceDriver<Text, TimeInfo, Text,                                           LongWritable>(reducer);

    }

 

    @Test

    public void testReduce () {

        List<TimeInfo> values = new ArrayList<TimeInfo>();

        values.add(new TimeInfo(1, 3));//一次3小時

        values.add(new TimeInfo(2, 5));//兩次總共5小時

        values.add(new TimeInfo(3, 7));//三次總共7小時

       //values作為444這個賣家的reduce輸入,

       //期望計算出平均為2小時

        reduceDriver.withReducer(reducer)

                  .withInput(new Text("444"), values)

                  .withOutput(new Text("444"),new  LongWritable(2))

                  .runTest();

    }

MapReduceDriver

    以下為Map和Reduce聯(lián)合測試的例子,

    private MapReduceDriver<LongWritable, Text, Text, TimeInfo, Text, LongWritable> mrDriver;

    private Map mapper;

    private Reduce reducer;

    @Before

    public void setUp() {

        mapper = new Map();

        reducer = new Reduce();

        mrDriver = new MapReduceDriver<LongWritable, Text, Text, TimeInfo,                    Text, LongWritable>(mapper, reducer);

    }

 

    @Test

    public void testMapReduce_3record_1user() {

       Text mapInputValue1 = new Text("……");

       Text mapInputValue2 = new Text("……");

       Text mapInputValue3 = new Text("……");

       //我們期望從以上三條Map輸入計算后,

       //從reduce輸出得到444這個賣家的平均時間為2小時.

        mrDriver.withInput(null, mapInputValue1)

           .withInput(null, mapInputValue2)

           .withInput(null, mapInputValue3)

           .withOutput(new Text("444"),new LongWritable(2))

           .runTest();

    }
 

增強(qiáng)MRUnit

         下面介紹為MRUnit框架增加了支持MultipleOutputs、從文件加載數(shù)據(jù)集和自動裝配等幾個特性,使它更加便于使用。

如何支持MultipleOutputs

         然而很多場景下我們需要使用MultipleOutputs作為reduce的多文件輸出,MRUnit缺少支持。分析源碼后為MRUnit增強(qiáng)擴(kuò)展了兩個DriverReduceMultipleOutputsDriverMapReduceMultipleOutputDriver來支持MultipleOutputs。

 

ReduceMultipleOutputsDriver

         ReduceMultipleOutputsDriverReduceDriver的增強(qiáng)版本,假設(shè)前面例子中的Reduce使用了MultipleOutputs作為輸出,那么Reduce的測試將出現(xiàn)錯誤。


 

使用ReduceMultipleOutputsDriver改造上面的測試用例(注意粗體部分),

private Reduce reducer;

    @Before

    public void setUp() {

        reducer = new Reduce();

       //注意這里ReduceDriver改為使用ReduceMultipleOutputsDriver

        reduceDriver = new ReduceMultipleOutputsDriver<Text, TimeInfo,                                     Text, LongWritable>(reducer);

    }

 

    @Test

    public void testReduce () {

        List<TimeInfo> values = new ArrayList<TimeInfo>();

        values.add(new TimeInfo(1, 3));//一次3小時

        values.add(new TimeInfo(2, 5));//兩次總共5小時

        values.add(new TimeInfo(3, 7));//三次總共7小時

       //values作為444這個賣家的reduce輸入,

       //期望計算出平均為2小時

        reduceDriver.withReducer(reducer)

               .withInput(new Text("444"), values)

               //Note

               //假設(shè)使用id(444)%8的方式來分文件

              //表示期望"somePrefix"+444%8這個collector將搜集到數(shù)據(jù)xxx

               . withMutiOutput ("somePrefix"+444%8,new Text("444"),new                                                     LongWritable(2))

              .runTest();

    }

 

 

 

MapReduceMultipleOutputDriver

         ReduceMultipleOutputsDriver類似,MapReduceMultipleOutputDriver用來支持使用了MultipleOutputsMap-Reduce聯(lián)合測試。MapReduceDriver一節(jié)中的例子將改為,

private MapReduceDriver<LongWritable, Text, Text, TimeInfo, Text, LongWritable> mrDriver;

    private Map mapper;

    private Reduce reducer;

    @Before

    public void setUp() {

        mapper = new Map();

        reducer = new Reduce();

       //改為使用ReduceMultipleOutputsDriver

        mrDriver = new ReduceMultipleOutputsDriver<LongWritable, Text, Text,               TimeInfo, Text, LongWritable>(mapper, reducer);

    }

 

    @Test

    public void testMapReduce_3record_1user() {

       Text mapInputValue1 = new Text("……");

       Text mapInputValue2 = new Text("……");

       Text mapInputValue3 = new Text("……");

       //我們期望從以上三條Map輸入計算后,

       //reduce輸出得到444這個賣家的平均時間為2小時.

        mrDriver.withInput(null, mapInputValue1)

           .withInput(null, mapInputValue2)

           .withInput(null, mapInputValue3)

           //表示期望"somePrefix"+444%8這個collector將搜集到數(shù)據(jù)xxx

           . withMutiOutput ("somePrefix"+444%8,new Text("444"),new                                              LongWritable(2))

           .runTest();

    }

 

 

 

如何從文件加載輸入

         從以上例子看到使用MRUnit需要重復(fù)寫很多類似的代碼,并且需要把輸入數(shù)據(jù)寫在代碼中,顯得不是很優(yōu)雅,如果能從文件加載數(shù)據(jù)則會方便很多。因此通過使用annotation和擴(kuò)展JUnit runner,增強(qiáng)了MRUnit來解決這個問題。

       改造上面的例子,使得map的輸入自動從文件加載,并且消除大量使用MRUnit框架API的代碼。

@RunWith(MRUnitJunit4TestClassRunner.class)

public class XXXMRUseAnnotationTest {

 

    //表示自動初始化mrDriver,并加載數(shù)據(jù)(如果需要)

    @MapInputSet

    @MapReduce(mapper = Map.class, reducer = Reduce.class)

     private MapReduceDriver<LongWritable, Text, Text, TimeInfo, Text, LongWritable> mrDriver;

 

    @Test

    @MapInputSet("ConsignTimeMRUseAnnotationTest.txt")//從這里加載輸入數(shù)據(jù)

    public void testMapReduce_3record_1user() {

           //只需要編寫驗證代碼

       mrDriver. withMutiOutput ("somePrefix"+444%8,new Text("444"),new LongWritable(2))

                                 .runTest();

    }

}

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多