HBase PerformanceEvaluation机制分析
来源:互联网 发布:mac双系统 没了一个 编辑:程序博客网 时间:2024/06/07 11:23
本文我们来分析一下hbase自带的测试工具——performanceEvaluation。该工具是hbase自带的性能压测工具,基本原因是是用多线程模拟多用户同时访问集群的情况。
运行hbase org.apache.hadoop.hbase.PerformanceEvaluation,可以看到关于performanceEvaluation的用法介绍,如下图中所示:
介绍一下关键的几个参数:
-nomapred,mo mapreduce,加入此参数表示采用本地多线程的方式去读写数据,默认不加,此时本地启动mapreduce任务方式去测试;
-table,待测试的标明;
-rows,在本地多线程读写的模式下,指定每个线程处理的数据行数;
-startRow,每个线程操作的起始数据key;
-compression,-blockEncoding,前者指明了压缩方式,默认是NONE,后者指定了block的encoding策略,默认也是none;
-writeToWAL,写入数据时的WLog落盘策略,包括SYNC_WAL和SKIP_WAL两种;
-multiGet,在RandomRead中,如果指定大于1,则一次返回多条数据,默认一次返回一条;
-inmemory,会将数据尽量放在内存中,默认是false,也即读操作是从磁盘返回,为了保证pe能够准确获取测量结果,建议保持为false;
-presplit和-splitPolicy,两者搭配使用,用于对测试表进行预切割;
-filterAll,这个参数我的理解是在测试scan功能时,加上此参数,则server端scan出来的结果不再返回给client端,用于单纯测试server端的性能;
performanceEvaluation提供了如下几种测试用例:
scanRange10,指定范围内(最多10个数内)的随机scan,其他与此相同,这里的startkey是totalRows之内随机生成的数,如下是startkey和endkey的生成代码:
protected Pair<byte[], byte[]> generateStartAndStopRows(int maxRange) { int start = this.rand.nextInt(Integer.MAX_VALUE) % opts.totalRows; int stop = start + maxRange; return new Pair<byte[],byte[]>(format(start), format(stop));}
sequentialRead/sequentialWrite,顺序读测试,顺序写测试;
randomRead/randomWrite,随机读&随机写,读写的数据量是上文中每个线程发送的数据量,每次读写的rowkey是totalRows内随机生成的数;
scan,scan测试,与普通scan相同,读出所有的数据,testScan的核心代码如下所示:
void testRow(final int i) throws IOException { if (this.testScanner == null) { Scan scan = new Scan(format(opts.startRow)); scan.setCaching(opts.caching); if (opts.addColumns) { scan.addColumn(FAMILY_NAME, QUALIFIER_NAME); } else { scan.addFamily(FAMILY_NAME); } if (opts.filterAll) { scan.setFilter(new FilterAllFilter()); } this.testScanner = table.getScanner(scan); } Result r = testScanner.next(); updateValueSize(r);}
注意这里加入的FilterAllFilter,如果指定了filterAll,那么scan出来的结果并不会返回给客户端,而是直接shortCut返回。
上面介绍了performanceEvaluation的用法,那么hbase是如何设计pe的线程模型,使得它可以对server端的性能进行压测的呢,这样的设计又对我们平时设计代码有什么样的启示,接着我们分析一下performanceEvaluation的编程实现。
程序的入口在performanceEvaluation的main方法中,main方法很简单,代码就两行,构造一个PerformanceEvaluation对象并作为参数传入ToolRunner的run方法中,该run方法会解析用户通过终端传入的参数,变成String[]类型的参数传入cool内部实现的run方法,这里就是performanceEvaluation中的run方法。
public int run(String[] args) throws Exception { try { LinkedList<String> argv = new LinkedList<String>(); argv.addAll(Arrays.asList(args)); TestOptions opts = parseOpts(argv); //检查参数&检查client线程数 Class<? extends Test> cmdClass = determineCommandClass(opts.cmdName); if (cmdClass != null) { runTest(cmdClass, opts); errCode = 0; } } catch (Exception e) { e.printStackTrace(); } return errCode;}run方法首先根据传入的String[]构造Options,完成参数和线程数数量的简单检查后,执行runTest进行测试,runTest中首先会检查table的状态,如果命令传入的table descriptor与table自身的属性不同(主要是检查table的split分区是否与用户指定的相同),那么就会将table删除重建,紧接着会根据用户是否指定了nomapred以决定是启用本地多线程执行测试还是运行mapreduce任务执行测试,runTest的代码如下所示:
private void runTest(final Class<? extends Test> cmd, TestOptions opts) throws IOException, InterruptedException, ClassNotFoundException { try(Connection conn = ConnectionFactory.createConnection(getConf()); Admin admin = conn.getAdmin()) { checkTable(admin, opts); } if (opts.nomapred) { doLocalClients(opts, getConf()); } else { doMapReduce(opts, getConf()); }}doMapReduece就是构造出mr任务然后提交到yarn上去运行,这里不再详述。我们重点看doLocalClients是如何实现的。doLocalClients是一个比较典型的多线程运行模型,关键的代码我们列在下面:
static RunResult[] doLocalClients(final TestOptions opts, final Configuration conf) throws IOException, InterruptedException { final Class<? extends Test> cmd = determineCommandClass(opts.cmdName); assert cmd != null; @SuppressWarnings("unchecked") Future<RunResult>[] threads = new Future[opts.numClientThreads]; RunResult[] results = new RunResult[opts.numClientThreads]; ExecutorService pool = Executors.newFixedThreadPool(opts.numClientThreads, new ThreadFactoryBuilder().setNameFormat("TestClient-%s").build()); final Connection con = ConnectionFactory.createConnection(conf); for (int i = 0; i < threads.length; i++) { final int index = i; threads[i] = pool.submit(new Callable<RunResult>() { @Override public RunResult call() throws Exception { TestOptions threadOpts = new TestOptions(opts); if (threadOpts.startRow == 0) threadOpts.startRow = index * threadOpts.perClientRunRows; RunResult run = runOneClient(cmd, conf, con, threadOpts, new Status() { @Override public void setStatus(final String msg) throws IOException { LOG.info(msg); } }); return run; } }); } pool.shutdown(); for (int i = 0; i < threads.length; i++) { try { results[i] = threads[i].get(); } catch (ExecutionException e) { throw new IOException(e.getCause()); } } final String test = cmd.getSimpleName(); Arrays.sort(results); long total = 0; for (RunResult result : results) { total += result.duration; } con.close(); return results;}
初始化了大小为numClientThreads的一个线程池pool,接着向线程池pool中通过submit投了numClientThreads个线程,这里多说一句,线程池的submit接口即可以接受Callable的线程也可以接受Runnable的线程,且无论是Callable还是Runnable为参数,调用submit之后,都可以返回一个Future对象,将来用户可以从Future中获知线程是否执行结束。
不同的是,Callable中的call方法可以返回一个结果,而Runnable的run则不能返回结果,如上面的代码所示,call方法中进一步调用了runOneClient,并将调用的结果包装为一个对象RunResult返回给客户端,客户端调用Future的get方法获得返回结果,根据RunResult的值计算avg、min&max等统计值。
重点看runOneClient的实现,runOneClient是每个单独线程所执行的方法,其中通过传入的参数反射构造了一个Test类,执行Test的test方法,也就是用户通过传入参数制定的test case用例,如ScanRange10、sequenceWrite等等,统计计算结果,并将各统计值构造一个RunResult类作为当前线程的返回结果返回给客户端。
static RunResult runOneClient(final Class<? extends Test> cmd, Configuration conf, Connection con, TestOptions opts, final Status status) throws IOException, InterruptedException { long totalElapsedTime; final Test t; try { Constructor<? extends Test> constructor = cmd.getDeclaredConstructor(Connection.class, TestOptions.class, Status.class); t = constructor.newInstance(con, opts, status); } catch (NoSuchMethodException e) { //省略 } catch (Exception e) { //省略 } totalElapsedTime = t.test(); return new RunResult(totalElapsedTime, t.getLatency());}
这一部反射构造test case实例实在是精彩,Test是抽象类,每个不同的Test用例都是Test类的一个实现,实现的关键在于testRow方法,文章前面已经例举了两个不同测试用例的testRow方法实现,这里不再赘述。
hbase的performanceEvaluation虽然只有一个类,但是同样用到了多线程、反射等java程序设计中经典的方法,对于java学习者研究其中的设计技巧受益很大。
阅读全文
0 0
- HBase PerformanceEvaluation机制分析
- hbase自带的测试工具PerformanceEvaluation
- [HBase] HBase Block Cache实现机制分析
- 深入分析HBase Compaction机制
- 深入分析HBase Compaction机制
- 深入分析HBase Compaction机制
- 深入分析HBase Compaction机制
- HBase Block Cache实现机制分析
- HBase Block Cache实现机制分析
- HBase的Block Cache实现机制分析
- HBase的Block Cache实现机制分析
- HBase的Block Cache实现机制分析
- HBase BlockCache机制讲解&源码分析
- [转]HBase BlockCache机制讲解&源码分析
- 深入分析HBase-RPC(Protobuf)实现机制
- HBase 源码分析1 – RPC机制 : 基础
- HBase源码分析2 – RPC机制:客户端
- 深入分析HBase-Phoenix执行机制与原理
- eclipse短信验证的布局
- Qt属性系统详解
- eclipse安装python插件(window环境)
- QT QGraphicsScene、QGraphicsItem、QGraphicsProxyWidget、QWidget间的事件传递
- Ubuntu安装配置串口通讯工具minicom&&cutecom
- HBase PerformanceEvaluation机制分析
- 机器学习笔记-Blending and Bagging
- 中英文姓名及手机号的正则表达式
- QGraphicsProxyWidget 与 QWidget 点滴
- ImageLoader多张图片&listview多条目加载
- Java基础学习之——接口的作用和抽象类的区别
- 操作系统基础
- Visual Studio2008 左侧显示行号
- bzoj2668 [cqoi2012]交换棋子