(原创)LoadRunner 中 调用dll
因为项目需要, 我这边打算做个动态从数据库取数据的dll,首先肯定的, 先从google上找找有没有现成的, 看到了kern zhang的帖子, 打算拿来用,因为都是转帖,不是很全, 搜了老半天,才知道他是用odbc来做的, 在自己的机子上配了odbc数据源,可是还是不能使用。在初始化hstmt(我猜想是sql句柄)失败,又打算想把他的dll反编译过来,改吧改吧的, 但C是不能反编译的,最后只能自己写了。
我这里的需求是:动态的从mysql数据库里取数据
所以我可以直接用mysql提供的C API
用visual C++编写,新建--工程--Win32 Dynamic-Link Libray,然后就可以用c语言写了。
需要注意的是,其一,我们要include mysql.h, 这个文件一般都在%mysql%/include下面 ,其二,我们还要引用#pragma comment(lib, "C:\\xampplite\\mysql\\lib\\opt\\libmysql.lib"),我们就可以用c api 提供的函数来编写了
1. 初始化:mysql_init(&mysql);
2. 建立连接:mysql_real_connect(&mysql,host,user,password,dbname,0,NULL,0)
3. Query: mysql_query(sql);
4. mysql_use_result(&mysql)
5. mysql_fetch_row(res)
6.根据自己的需要,对取回的结果进行处理
编译完dll后, 将它和loadrunner脚本放在同个目录下, 然后我们还要将libmysql.dll也放到该目录下才可以。
lordrunner中,默认是按照run Vuser as a thread 运行,我写好的dll在generator中测试通过,但在Controller 中却失败,分析原因主要是指针访问了不该访问的地方,还有数据库初始化失败,查阅了很多资料,应该是我的公共变量引起的,在run Vuser as a thread 下,造成了变量不安全的状况。 也想了很多解决办法,问了C组的人, 他们说这是个很麻烦的事情,不建议我为了这点事花很长时间去解决。 最后,我在loadrunner里改变了运行模式,改为run Vuser as a process,结果一切正常。
但我又开始担心用这种模式做,会不会影响性能。找到一篇文章(http://rdc.taobao.com/blog/qa/?p=2046)
如果按进程方式运行每个Vuser,则对于每个Vuser实例,都将启动一驱动程序并将其加载到内存中,如果设置了10个Vuser,则在任务管理器中出现10个驱动程序,将多个驱动程序加载到内存中会占用大量RAM(随机存取存储器)及其他系统资源,这就限制了可以在任一负载生成器上运行的Vuser的数量。
如果按线程运行每个Vuser,Controller为每50个Vuser(默认情况下)仅启动驱动程序(如mdrv.exe)的一个实例。这些线程Vuser将共享父驱动进程的内存段。这就消除了多次重新加载驱动程序的需要,节省了大量内存空间,从而可以在一个负载生成器上运行更多的Vuser。
WEB性能测试常用的是线程的方式,但线程的资源是从进程资源中分配出来的,因此同一个进程中的多个线程会有共享的内存空间,这样可能会引起多个线程的同步问题,调度不好,就会出问题,如A线程要用的资源就必须等待B线程释放,而B也在等待其他资源释放才能继续。所以线程方式也非适合于所有场景,有文章描述说线程只适合于安全协议。
所以我们可以认为选多线程的方式是可在每个负载生成器能运行更多个Vuser。
最后大家或许还会关心这两种方式对服务端造成的压力是否有区别,偶看了多篇文章的描述和运行数据得出,对服务器来说,只要是设置的用户数都在正常工作,而速度不会受到本地硬件的影响,对服务器的压力应该是一样的。
目前打算按照 run Vuser as a process来做,如果将来能有更好的解决办法,再换成thread
Loadrunner脚本
vuser_init()
{
int i;
char *host = "192.168.22.193";
char *user = "test";
char *password = "test";
char *dbname = "test";
lr_load_dll("test.dll");
i = Db_init(host,user,password,dbname);
if(!i){
lr_error_message("init db error");
return -1;
}
return 0;
}
/***
这块要说明下,之所以设置int i=5和后面的i=i+5的目的,主要是为了解决我们的参数在每个vuser中不一样,而且是按照我们的要求递增的。 我知道在loadrunner里有设置可以解决每个vuser使用的参数不一样,他是把参数分成几个块,每个vuser去对应的块里取相应的数据。如果我们有从1-100的参数, 5个vuser,那么一开始,vuser1会取1,vuser2会取21。。。。vuser5会取81。但我们的实际情况是,刚开始运行时,数据库里并没有81,41这些数据。我们要让这些vuser从小到大依次读取参数,如vuser1取1,vuser2取2,依次类推。到vuser1的第二次循环,它会取6
***/
//concurrent user count
int i =5;
Action()
{
int id;
char str[10];
//char *tmp;
char sql[]="select textContent from cdr_mmvd where id =";
id = atoi(lr_eval_string("{id}")) + i;
//itoa(id,str,10);
strcat(sql,itoa(id,str,10));
i=i+5;
//lr_log_message("output is %s \n",str);
lr_log_message("output is %s \n",Query(sql));
FreeResult();
//lr_think_time(1);
return 0;
}
vuser_end()
{
Close();
return 0;
}
下面是我的dll源码。我对C不是很熟悉,所以写的比较简单。
#include "lib.h"
#include <stdio.h>
#include <winsock.h>
#include <string.h>
#include "C:\xampplite\mysql\include\mysql.h"
#pragma comment(lib, "C:\\xampplite\\mysql\\lib\\opt\\libmysql.lib")
int Db_int(char *, char *, char *, char *);
char *Query(char *);
void FreeResult();
void Close();
FILE *fp;
MYSQL mysql;
MYSQL_RES *res;
MYSQL_ROW row;
char key[6];
int Db_init(char *host,char *user,char *password,char *dbname)
{
if((fp = fopen("dll.log","w+"))==NULL)
printf("FILE not be opened!");
mysql_init(&mysql);
if(!mysql_real_connect(&mysql,host,user,password,dbname,0,NULL,CLIENT_MULTI_RESULTS)){
fprintf(fp,"mysql connection failed: %s\n",mysql_error(&mysql));
return 0;
}
else
return 1;
}
char *Query(char *sql){
char *tmp;
fprintf(fp,"start to query\n");
if (!mysql_query(&mysql,sql)){
res = mysql_use_result(&mysql);
row = mysql_fetch_row(res);
tmp = strtok(row[0],"=");
tmp = strtok(NULL,"=");
strncpy(key,tmp,6);
key[6] = '\0';
return key;
}
else{
fprintf(fp,"query failed: %s\n",mysql_error(&mysql));
return "1";
}
}
void FreeResult(){
mysql_free_result(res);
}
void Close(){
mysql_close(&mysql);
mysql_library_end();
fprintf(fp,"close mysql connection. finished.\n");
fclose(fp);
}