HBase应用笔记:MapReduce测试实战(转自:Taobao QA Team)
引言
在上篇文章中介绍了如何利用MapReduce来分析HBase中的数据,并通过代码示例来演示。老实说,当我写完那段代码时我一点信心都没有,我非常想知道这个job能否正常执行,结果是否符合预期,怎么办呢?一个常见的流程可能是这样的:
1. 申请HBase环境的访问权限(或者自己搭一套)
2. 创建blog表和tag_friend表,插入一些测试数据
3. 将Job类及相关类库打成jar包,并上传到HBase集成环境的gateway上,开始运行该Job
4. 查看Job运行情况,完成后查tag_friend表看数据是否符合预期。
5. 如果发现程序有问题,修改程序,重复第3,4步。
哦No,这太繁琐了,我只是玩玩看我的程序大体对不对而已,而且这种方式不能debug,如果出现诡异问题需要借助大量System.out或log输出来定位,这很不爽,有更简便的方法吗?
Hadoop/HBase Mini Cluster介绍
如何不安装Hadoop、HBase环境,只要有JDK,就可以跑起来一个MapReduce的例子,就可以执行HBase的创建表及增删改查操作?
Hadoop、HBase提供了三个类来模拟集群环境:
org.apache.hadoop.hdfs. MiniDFSCluster,模拟DFS集群
org.apache.hadoop.mapred.MiniMRCluster,模拟Map-Reduce集群
org.apache.hadoop.hbase. MiniHBaseCluster,模拟HBase集群
三个类都是本机单进程的,通过构造器参数来设置关键配置和参数。
这三个类在Hadoop及HBase项目本身的单元测试中被大量使用,但如果你是一名业务开发人员,直接使用这些类来做业务测试会发现稍显重复或繁琐,因为有很多的Hadoop/HBase本身的参数在此并不关心,需要的只是一个模拟集群环境,然后可以在此环境中运行业务(MapReduce、HBase)程序来验证功能。为此我们提供了itest-hadoop版本来简化这个问题。
iTest-hadoop介绍
关于iTest的介绍可以参考这里,http://www.taobaotest.com/itest。
iTest-hadoop是iTest的一个子项目,目前处于发展阶段,用于Hadoop/HBase业务开发单元测试.使用iTest-hadoop来测试HBase Mapreduce非常简单,只需要以下几步:
- 1. 加入依赖
加入itest-hadoop依赖
<dependency>
<groupId>com.taobao.test</groupId>
<artifactId>itest-hadoop</artifactId>
<version>1.3.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
加入hadoop-test依赖,注意版本号必须跟hadoop-core的版本一致
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-test</artifactId>
<version>0.20.2</version>
<scope>test</scope>
</dependency>
2. 测试(基)类继承ITestHBaseTestCase
public class FindFriendTest extends ITestHBaseTestCase {}
3. 用@ITestHadoopConfiguration注解加载HBase文件
默认会加载classpath下hbase-site.xml文件,也可以通过locations属性加载其他文件。iTest-hadoop提供了默认的hbase-site.xml文件随jar包一起发布,因此一般无需自己再准备一个文件。
@ITestHadoopConfiguration public class FindFriendTest extends ITestHBaseTestCase {}
4. 用@ITestHBaseClusterStarter启动模拟HBase集群
该注解会启动DFS、zookeeper和HBase模拟集群,默认1个master,1个slave,可以通过numMasters和numSlaves来设置master和slave的数量。
@ITestHadoopConfiguration @ITestHBaseClusterStarter public class FindFriendTest extends ITestHBaseTestCase {}
5. 现在集群环境有了,在测试方法里准备数据,提交作业,运行完MapReduce后验证结果(用前面介绍的Java api访问HBase)即可。
需要注意的是如果开发环境为Windows,需安装cygwin,并将其bin目录设置到环境变量path里。
下图是一个我在eclipse里dubug截图,可以看到在eclipse里运行了MapReduce作业,在eclipse Console里可以看到作业的运行情况,而且可以轻松debug到Map和Reduce代码。
完整测试代码示例
@ITestHadoopConfiguration// 加载Hadoop @ITestHBaseClusterStarter// 启动完整的HBase集群 /HBase配置文件 public class FindFriendTest extends ITestHBaseTestCase { private HTable blogTable; private HTable tagFriendTable; @Test public void test() throws IOException, InterruptedException,ClassNotFoundException { creatTable(); initData(); submitJob(); verifyMapReduceResult(); deleteTable(); } private void creatTable() throws IOException { blogTable = createTable("blog", new String[] { "article", "author" }); tagFriendTable = createTable("tag_friend", "person"); } private void initData() throws IOException { Put put = new Put(Bytes.toBytes("1")); put.add(Bytes.toBytes("article"), Bytes.toBytes("title"),Bytes.toBytes("Head First HBase")); put.add(Bytes.toBytes("article"), Bytes.toBytes("content"),Bytes.toBytes("HBase is the Hadoop database. Use it when you need random, realtime read/write access to your Big Data.")); put.add(Bytes.toBytes("article"), Bytes.toBytes("tags"),Bytes.toBytes("HBase,NoSQL,Hadoop")); put.add(Bytes.toBytes("author"), Bytes.toBytes("name"), Bytes.toBytes("hujinjun")); put.add(Bytes.toBytes("author"), Bytes.toBytes("nickname"),Bytes.toBytes("yedu")); blogTable.put(put); Put put2 = new Put(Bytes.toBytes("2")); put2.add(Bytes.toBytes("article"), Bytes.toBytes("tags"),Bytes.toBytes("Hadoop")); put2.add(Bytes.toBytes("author"), Bytes.toBytes("nickname"),Bytes.toBytes("heyun")); blogTable.put(put2); Put put3 = new Put(Bytes.toBytes("3")); put3.add(Bytes.toBytes("article"), Bytes.toBytes("tags"),Bytes.toBytes("hbase,NoSql")); put3.add(Bytes.toBytes("author"), Bytes.toBytes("nickname"),Bytes.toBytes("shenxiu")); blogTable.put(put3); } private void submitJob() throws IOException, InterruptedException,ClassNotFoundException { Scan scan = new Scan(); scan.addColumn(Bytes.toBytes("author"), Bytes.toBytes("nickname")); scan.addColumn(Bytes.toBytes("article"), Bytes.toBytes("tags")); Job job = new Job(blogTable.getConfiguration()); TableMapReduceUtil.initTableMapperJob(Bytes.toString(blogTable.getTableName()), scan,FindFriend.Mapper.class, ImmutableBytesWritable.class,ImmutableBytesWritable.class, job); TableMapReduceUtil.initTableReducerJob(Bytes.toString(tagFriendTable.getTableName()),FindFriend.Reducer.class, job); FileOutputFormat.setOutputPath(job, new Path("test")); job.waitForCompletion(true); } private void verifyMapReduceResult() throws IOException { Scan scanTagFriend = new Scan(); scanTagFriend.addColumn(Bytes.toBytes("person"),Bytes.toBytes("nicknames")); HTable table = new HTable(new Configuration(getConfiguration()),"tag_friend"); ResultScanner rs = table.getScanner(scanTagFriend); int i = 0; String tag = null; String nicknames = null; for (Result result : rs) { for (KeyValue kv : result.list()) { tag = Bytes.toString(kv.getRow()); nicknames = Bytes.toString((kv.getValue())); } i++; switch (i) { case 1: assertThat(tag, is("hadoop")); assertThat(nicknames, is("yedu,heyun")); break; case 2: assertThat(tag, is("hbase")); assertThat(nicknames, is("yedu,shenxiu")); break; case 3: assertThat(tag, is("nosql")); assertThat(nicknames, is("yedu,shenxiu")); break; } } assertThat(i, is(3)); } private void deleteTable() throws IOException { deleteTable("blog"); deleteTable("tag_friend"); } }
小结
本文介绍了如何在单机模拟集群环境下运行HBase MapReduce任务进行测试和Debug,这非常必要,尽快在本机发现一些代码逻辑问题而不是放到分布式集群环境中再来验证以降低复杂度节省时间。除了MapReduce测试外,其他的一些HBase表相关的业务操作测试可以参考前面一篇文章介绍的Java api访问HBase知识来进行测试。这是本系列最后一篇文章了,关于业务开发测试HBase相关还有很多东西可挖,比如快速批量导数据,数据从其他载体(跨集群的DFS或RDBMS等)迁移等,iTest-hadoop也会不断发展完善。