Jmeter+H2 Database动态部署JAR包到代理端

Jmeter开源性能测试工具某些功能不如某些商业化工具方便,但免费是其巨大优势,况且某些方便的功能也可以通过我们“自食其力”地实现。本文涉及:数据库中BLOB字段的处理;Jmeter与H2 Database在性能测试中的使用;Jmeter分布式测试。

1、需求背景

最近因项目性能测试需要,学习了Jmeter这个开源工具。但发现其某些地方确实不如LoadRunner方便。比如如下两个问题:

1)        当业务处理代码(JAVA请求代码)需要变更时,新的Jar包没法统一更新(部署)到各个代理端;

2)        全局的序列化参数(即需要唯一取值的参数)无法进行统一同步,因为不同的代理端运行在不同的JVM之上,代码的synchronized仅能在单独的JVM生效。

对比LoadRunner,第一个问题,脚本代码的任何更新只需要在Controller端刷新一下就可以同步到代理机了;第二个问题,LoadRunner使用参数block就可以全局管理那些需要唯一取值的参数。

还好,在JAVA类请求中,使用“外挂”数据库可以解决以上两个问题。这里介绍下H2 Database,一个嵌入式开源数据库,不需安装,整个实现环境就一个jar包,文档及下载地址是:http://www.h2database.com/


2、解决方法

1)      结构图示

先画个图说明一下解决方法:



其中jar包文件以BLOB的形式存放在H2数据库表extjars(batchno,putedtime,filename,content)中。


2)      Blob对象处理

Blob对象处理,即服务端文件入库和代理端读取文件的过程和方法。先创建一个表来保存不同版本的jar文件:

Create table extjars (batchno varchar(26),putedtime varchar(26),filename varchar(260),contentblob);

Blob入库

向表中插入BLOB对象时,新的JDBC6已经可以直接插入blob数据,而不像老版本的jdbc需要先insert一个空的blob然后再获取这个对象并写入数据。

下面是插入Blob字段(这里的blob字段就是存放jar包的)的主要代码:

/**

     * 将文件以二进制Blob对象放入表中

     * @param batchno

     * @param filename

     */

public voidinsertAJarFile(Connection conn,String batchno,String filename){

                   String curtime =CommonUtil.getCurStringTime();

                   String sqlInsert ="insert into extjars(batchno,putedtime,filename,content)values(?,?,?,?)";                

                   Connection conn = null;           

                   try {

                            //创建一个blob对象

                            Blob blob =conn.createBlob();

                            //打开输入文件流

                            InputStream is = newFileInputStream(new File(filename));

//设置流写入开始位置

            OutputStream out =blob.setBinaryStream(1); 

                            //设置缓冲大小10Mb

            byte[] temp = newbyte[1024000]; 

            int length; 

                            //开始写入

            while ((length = is.read(temp)) !=-1) { 

                  out.write(temp, 0, length); 

            } 

            is.close();

            out.flush();

            out.close();

            //插入普通字段和blob字段

            PreparedStatement pstmt =conn.prepareStatement(sqlInsert); 

            pstmt.setString(1, batchno); 

            pstmt.setString(2, curtime);

            pstmt.setString(3, filename); 

            pstmt.setBlob(4, blob);

            pstmt.executeUpdate();

            pstmt.close();

            conn.commit();

            //释放blob资源

            blob.free();

            conn.close();

                   } catch (Exception e) {

                            e.printStackTrace();

                   } 

         }

Blob读取

插入整个文件到表中后,代理端(Slave)使用下面这段代码读取Jar文件到本地磁盘:

/**

     * 从表中获取最新版本文件

     * @param conn数据库连接句柄

     * @param jarName要下载文件的名字

     * @param storePath存放路径

     * @return

     */

public void getLastJarFile(Connetionconn,String jarName,String storePath){               

                   //查询最大版本号的文件信息

                   String sqlSelect = "SELECTbatchno, putedtime,filename,content FROM EXTJARS where PUTEDTIME = (selectmax(putedtime) from extjars)";

                   if(!jarName.equals("")&&null!= jarName){

                            //查询指定文件名最大版本号的记录信息

                            sqlSelect = "SELECTbatchno, putedtime,filename,content FROM EXTJARS where batchno = (SELECTMAX(batchno) FROM EXTJARS where filename like '%"+jarName+"%') andfilename like '%"+jarName+"%'";

                   }       

                   String batchno = "";

                   String putedtime = "";

                   String fullname = "";

                   String filename = "";

                   try {                   

                            Statement stmt = conn.createStatement(); 

            ResultSet rs = stmt.executeQuery(sqlSelect);

            Blob blob = null; 

            while(rs.next()) 

            { 

                     batchno= rs.getString(1);

                     putedtime= rs.getString(2);

                     fullname  = rs.getString(3);

                     if(fullname.indexOf("/")>=0){

                               filename=fullname.substring(fullname.lastIndexOf("/")+1);

                     }

                                     //获取结果集中的Blob对象

                blob = rs.getBlob(4); 

            } 

                            //将流形式的blob对象放入byte数组中

            byte[] temp = new byte[(int)blob.length()]; 

            InputStream in = blob.getBinaryStream(); 

            in.read(temp);

            //将缓存的byte写到文件

            storePath = storePath + "/"+ filename;

            File file = new File(storePath); 

            FileOutputStream fout = newFileOutputStream(file); 

            fout.write(temp);

            fout.flush();

            in.close(); 

            fout.close(); 

            blob.free();

            conn.close();

                   } catch (Exception e) {

                            e.printStackTrace();

                   }              

         }

 

这里将封装的方法打包后放入<jmeterPath>/lib/ext目录下,并在主控端建立一个Java请求的测试计划,专门用于更新代理端的jar包。此过程省略。


3)      全局序列化参数

这个问题的解决借鉴LoadRunner里“uniquenumber”的block方式,按VU数量进行分区,每个VU(所有代理机上的)在分区内的数据进行递增。



分块数据存储结构为:create table xxx_paramName(HOST_VUID varchar(20), BASE_NUMB, varchar(20),LAST_NUMB varchar(20));

HOST_VUID:表示线程标志(VUID),主键唯一;

BASE_NUMB:表示分块数据初始值;

LAST_NUMB:表示分块数据被使用过的最大值(下次的起点);

这部分代码比较简单,即读取配置文件的初始值(需要参数化的变量,变量的初始值,VU数量),分块入库。

重点说一下H2数据库在这里的特殊作用:

启动TCP连接

启动TCP连接,为客户端(代理机)提供数据获取和更新服务。

□服务端启动代码如下:

/**

     * 创建一个H2数据库TCP服务

     * @param port 本地端口

     */

         public static Server startH2TCPServer(Stringport) {

                   Server server = null;

                   try {

                            server = Server.createTcpServer(newString[] { "-tcpPort", port, "-tcpAllowOthers" })

                                               .start();

                   } catch (SQLException e) {                          

                            e.printStackTrace();

                            throw newRuntimeException(e);

                   }

                   return server;

         }

□客户端连接代码如下:

/**

     * 通过TCP方式连接到H2数据库

     * @param ipport

     * @param dbtype

     * @param dbname

     * @returnConnection

     */

         public static Connection createH2TCPConnection(Stringipport,

                            String dbtype,String dbname) {

                   String sourceURL = null;

                   Connection conn = null;

                   try {

                            //注意TCP连接的写法

                            if (dbname.isEmpty()){

                                     sourceURL ="jdbc:h2:tcp://" + ipport + "/mem:" + dbname;

                            } else {

                                     sourceURL ="jdbc:h2:tcp://" + ipport + "/~/" + dbname;

                            }

                            Class.forName("org.h2.Driver");

                            //连接用户和密码要根据约定修改

                            conn =DriverManager.getConnection(sourceURL, "user", "password");

                            CommonUtil.printLog2Console("Databaseconnected successfully!");

                   } catch (Exception e) {

                            e.printStackTrace();

                            return null;

                   }

                   return conn;

         }

启动WEB连接

H2数据库另一个极好的功能是可以通过浏览器的方式连接并编辑。

□服务端启动代码如下:

/**

     * 启动一个H2 WEB Server,其他客户端可以通过IE访问

     * @param port

     * @return

     */

         public static Server startH2WEBServer(Stringport) {

                   Server server = null;

                   try {

                            server = Server.createWebServer(newString[]{"-webPort",port, "-webAllowOthers" }).start();

                            System.out.println("DatabaseServer(WEB) Startup Success : " + server.getURL());

                   } catch (SQLException e) {

                            System.out.println ("Startthe database error : " + e.toString());

                            e.printStackTrace();

                            throw newRuntimeException(e);

                   }

                   return server;

         }


□使用IE连接:





Jmeter中本框架的使用

下面是Jmeter中使用DeBlock的calss的必须实现的代码:

 

publicclass YourClassName extends AbstractJavaSamplerClient {

   //VU序号,根据VU数递增

private static vuid = 0;

//vuid组成VU(线程)名字

   private String VUName = "";

   //需要在全部线程上同步的序列化变量

   private static int msgid1 = 0;

//当前线程迭代计数

private int loops = 0;

//管理端IP和端口

private String DOBLOCK_SERVER = "";

  

public ArgumentsgetDefaultParameters() {

     Argumentsparams = new Arguments();

     params.addArgument("DOBLOCK_SERVER","192.168.219.194:50000");

     returnparams;

}

 

   public void setupTest(JavaSamplerContextarg0) {

    try {     

       DOBLOCK_SERVER= arg0.getParameter("DOBLOCK_SERVER");

       //连接到服务端

DeBlock de = newDeBlock(DOBLOCK_SERVER);

        //设置VU标志(线程唯一ID

       VUName= de.setVUName(++vuid);

       //根据VUNAME和参数名msgid1取得开始值, system_tag:与业务系统相关的标志

       Stringstart = de.getBlockBase(system_tag, "msgid1", VUName);

       msgid1 = Integer.parseInt(start);

    } catch (Exception e) {

       e.printStackTrace();

    }

      //其他初始化代码

      .........

   }

  

  

   //迭代中使用msgid1

   public SampleResultrunTest(JavaSamplerContext arg0) {

        //迭代代码

        msgid1++;

        loops++;

   }

  

   //测试结束时调用

   publicvoid teardownTest(JavaSamplerContextarg0) {

   try {

        DeBlockde = new DeBlock(DOBLOCK_SERVER);

        de.updateLastNumb(system_tag,"msgid1", VUName,loops);

   }catch (Exception e) {

        e.printStackTrace();

   }          

   //其他代码

   ...............

   }

}

 

本篇完。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值