qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

PHP数据库长连接mysql_pconnect的细节

   PHP的MySQL持久化连接,美好的目标,却拥有糟糕的口碑,往往令人敬而远之。这到底是为啥么。近距离观察后发现,这家伙也不容易啊,要看Apache的脸色,还得听MySQL指挥。
  对于作为Apache模块运行的PHP来说,要实现MySQL持久化连接,首先得取决于Apache这个web服务器是否支持Keep-Alive。
  Keep-Alive
  Keep-Alive是什么东西?它是http协议的一部分,让我们复习一下没有Keep-Alive的http请求,从客户在浏览器输入一个有效url地址开始,浏览器就会利用socket向url对应的web服务器发送一条TCP请求,这个请求成功一次就得需要来回握三次手才能确定,成功以后,浏览器利用socket TCP连接资源向web服务器请求http协议,发送以后就等着web服务器把http返回头和body发送回来,发回来后浏览器关闭socket连接,然后做http返回头和body的解析工作,最后呈现在浏览器上的就是漂亮的页面了。这里面有什么问题呢?TCP连接需要三次握手,也就是来回请求三次方能确定一个TCP请求是否成功,然后TCP关闭呢?来回需要4次请求才能完成!每次http请求就3次握手,4次拜拜,这来来回回的不嫌累啊,多少时间和资源都被浪费在socket连接关闭上了,能不能一次socket TCP连接发送多次http请求呢?于是Keep-Alive就应运而生,http/1.0里需要客户端自己在请求头加入Connection:Keep-alive方能实现,在这里我们只考虑http1.1了,只需要设置一下Apache,让它默认就是Keep-Alive持久连接模式(Apache必须1.2+才能支持Keep-Alive)。在httpd.conf里找到KeepAive配置项,果断设置为On,MaxKeepAliveRequests果断为0(一个持久TCP最多允许的请求数,如果过小,很容易在TCP未过期的情况下,达到最大连接,那下次连接就又是新的TCP连接了,这里设置0表示不限制),然后对于mysql_pconnect最重要的选项KeepAliveTimeout设置为15(表示15秒)。
  好了,重启Apache,测试一下,赶紧写行东西:
  1<?php
  2   echo "Apache进程号:". getmypid();
  3?>
  很简单,获取当前PHP执行者(Apache)的进程号,用浏览器浏览这个页面,看到什么?对,有看到一串进程号数字,15秒内,连续刷新页面,看看进程号有无变化?木有吧?现在把手拿开,交叉在胸前,度好时间,1秒,2秒,3,...15,16。好,过了15秒了,再去刷新页面,进程号有没有变化?变了!又是一个新的Apache进程了,为什么15秒后就变成新的进程了?记得我们在Apache里设置的KeepAliveTimeout吗?它的值就是15秒。现在我们应该大致清楚了,在web服务器默认打开KeepAlive的情况下,客户端第一次http成功请求后,Apache不会立刻断开socket,而是一直监听来自这一客户端的请求,监听多久?根据KeepAliveTimeout选项配置的时间决定,一旦超过这一时间,Apache就会断开socket了,那么下次同一客户端再次请求,Apache就会新开一个进程来相应。所以我们之前15内不停的刷新页面,看到的进程号都是一致的,表明是浏览器请求给了同一个Apache进程。
  浏览器是怎么知道不需要重新进行TCP连接就可以直接发送http请求呢?因为http返回头里就会带上Connection:keep-alive,Keep-alive:15两行,意思就是让客户端浏览器明白,这次socket连接我这边还没关闭呢,你可以在15内继续使用这个连接,并发送http请求,于是乎浏览器就知道应该怎么做了。
  PHP怎么做
  那么,PHP的MySQL连接资源是怎么被hold住的呢,这需要查看PHP的mysql_pconnect的函数代码,我看了下,大概的做法就是mysql_pconnect根据当前Apache进程号,生成hash key,找hash表内有无对应的连接资源,没有则推入hash表,有则直接使用。有些代码片段可以说明(具体可查看PHP5.3.8源码ext/mysql/PHP_mysql.c文件690行PHP_mysql_do_connect函数)
#1.生成hash key

user=php_get_current_user();//获取当前PHP执行者(Apache)的进程唯一标识号
//hashed_details就是hash key
hashed_details_length = spprintf(&hashed_details, 0, "MySQL__%s_", user);
#2.如果未找到已有资源,就推入hash表,名字叫persistent_list,如果找到就直接使用
/* try to find if we already have this link in our persistent list */
if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_length+1, (void **) &le)==FAILURE) {
/* we don't */
...
...
/* hash it up(推入hash表) */
Z_TYPE(new_le) = le_plink;
new_le.ptr = mysql;
if (zend_hash_update(&EG(persistent_list), hashed_details, hashed_details_length+1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL)==FAILURE) {
...
...
}
}
else
{/* The link is in our list of persistent connections(连接已在hash表里)*/
...
...
mysql = (PHP_mysql_conn *) le->ptr;//直接使用对应的sql连接资源
...
...
}

zend_hash_find比较容易看明白,原型是zend_hash_find(hash表,key名,key长,value);如果找到,value就有值了。
  MySQL的wait_timeout和interactive_timeout
  说完Keep-Alive,该到MySQL家串串门了,说的是mysql_pconnect,怎么能绕开MySQL的设置。影响mysql_pconnect最重要的两个参数就是wait_timeout和interactive_timeout,它们是什么东西?先撇一边,首先让我们把上面的代码改动一下PHP代码
<?php
$conn = mysql_pconnect("localhost","root","123456") or die("Can not connect to MySQL");
echo "MySQL线程号:". MySQL_thread_id($conn). "<br />";
echo "Apache进程号". getmypid();
?>
  以上的代码没啥好解释的,让我们用浏览器浏览这个页面,看到什么?看到两个显眼的数字。一个是MySQL线程号,一个是Apache进程号,好了,15秒后再刷新这个页面,发现这两个id都变了,因为已经是新的Apache进程了,进程id是新的,hash key就变了,PHP只好重新连接MySQL,连接资源推入persistent list。如果15内刷新呢?Apache进程肯定不变,MySQL线程号会变吗?答案得问MySQL了。首先这个MySQL_thread_id是什么东西?shell方式登录MySQL后执行命令'show processlist;',看到了什么?
  [sql] view plaincopy
  mysql> show processlist;
  +-----+------+-----------+------+--------+-----+------+-----------------+
  | Id  | User | Host      | db   | Command| Time| State| Info            |
  +-----+------+-----------+------+--------+-----+------+-----------------+
  | 348 | root | localhost | NULL | Query  |    0| NULL | show processlist|
  | 349 | root | localhost | NULL | Sleep  |    2|      | NULL            |
  +-----+------+-----------+------+--------+-----+------+-----------------+
  发现了很重要的信息,这个processlist列表就是记录了正在跑的线程,忽略Info列为show processlist那行,那行是你当前shell登录MySQL的线程。PHP连接MySQL的线程就是Id为349那行,如果读者自己做测试,应该知道这个Id=349在你的测试环境里是另外一个值,我们把这个值和网页里输出的MySQL_thread_id($conn)做做比较,对!他们是一样的。接下来最重要的是观察Command列和Time列,Command = Sleep,表明什么?表明我们mysql_pconnect连接后就一直在sleep,Time字段就告诉我们,这个线程Sleep了多久,那么Sleep了多久这个线程才能作废呢?那就是wait_timeout或者interactive_timeout要做的工作了,他们默认的值都是8小时,天啊,太久了,所以如果说web服务器关掉KeepAlive支持,那个这个processlist很容易就被撑爆,就爆出那个Too many connections的错误了,max_connectiosns配置得再多也没用。为了观察这两个参数,我们可以在MySQL配置文件my.cnf里设置这两个值,找到[MySQLd]节点,在里面设置多两行
  1 interactive_timeout = 60
  2 wait_timeout        = 30
  配置完后,重启MySQL,shell登录MySQL,这时候show processlist可以发现只有当前线程。然后运行那个带有mysql_pconnect的PHP页面,再回来MySQL端show processlist可发现,多了一个Commond为Sleep的线程,不停的show processlist(方向键上+enter键)观察Time列的变化2,5,10...14!,突然那个Sleep线程程被kill掉了,咋回事,还没到30秒呢,噢!忘了修改一下Apache keepalive的参数了,把KeepAliveTimeOut从15改成120(只为观察,才这么改),重启Apache。刷新那个页面,好,开始不停的show processlist,2..5..10..14,15,..20...26....28,29!线程被kill,这次是因为wait_timeout起了作用,浏览器那边停了30秒,30内如果浏览器刷新,那这个Time又会从0开始计时。这种连接不属于interactive connection(MySQL shell登录那种连接就属于interactive connection),所以采用了wait_timeout的值。如果mysql_pconnect的第4个参数改改呢
<?php
$conn = mysql_pconnect('localhost','root','123456',MySQL_CLIENT_INTERACTIVE);
echo "MySQL线程号:".MySQL_thread_id($conn)."<br />";
echo "Apache进程号:".getmypid();
?>
  刷新下页面,MySQL那边开始刷show processlist,这回Time > 30也不会被kill,>60才被kill了,说明设置了MySQL_CLIENT_INTERACTIVE,就会被MySQL视为interactive connection,那么这次PHP的MySQL连接在120秒内未刷新的情况下,何时作废将取决于MySQL的interactive_timeout的配置值。
  总结
  PHP的mysql_pconnect要达到功效,首先必须保证Apache是支持keep alive的,其次KeepAliveTimeOut应该设置多久呢,要根据自身站点的访问情况做调整,时间太短,keep alive没啥意义,时间太长,就很可能为一个闲客户端连接牺牲很多服务器资源,毕竟hold住socket监听进程是要消耗cpu内存的。最后Apache的KeepAliveTimeOut配置得和MySQL的time out配置要有个平衡点,联系以上的观察,假设mysql_pconnect未带上第4个参数,如果Apache的KeepAliveTimeOut设置的秒数比wait_timeout小,那真正对mysql_pconnect起作用的是Apache而不是MySQL的配置。这时如果MySQL的wait_timeout偏大,并发量大的情况下,很可能就一堆废弃的connection了,MySQL这边如果不及时回收,那就很可能Too many connections了。可是如果KeepAliveTimeOut太大呢,又回到之前的问题,所以貌似Apache。KeepAliveTimeOu不要太大,但比MySQL。wait_timeout 稍大,或者相等是比较好的方案,这样可以保证keep alive过期后,废弃的MySQL连接可以及时被回收。

posted @ 2013-10-22 10:04 顺其自然EVO 阅读(179) | 评论 (0)编辑 收藏

Linux操作系统文件系统基础知识详解

 一 、Linux文件结构
  文件结构是文件存放在磁盘等存贮设备上的组织方法。主要体现在对文件和目录的组织上。
  目录提供了管理文件的一个方便而有效的途径。
  Linux使用标准的目录结构,在安装的时候,安装程序就已经为用户创建了文件系统和完整而固定的目录组成形式,并指定了每个目录的作用和其中的文件类型。
  /根目录
  ┃
  ┏━━┳━━━┳━━━┳━━━╋━━━┳━━━┳━━━┳━━━┓
  ┃   ┃      ┃     ┃     ┃     ┃      ┃     ┃     ┃
  bin  home    dev     etc     lib     sbin    tmp      usr    var
  ┃                               ┃
  ┏━┻━┓     ┏━━┳━━┳━━┳━┻━┳━━┓
  ┃      ┃    ┃   ┃    ┃    ┃     ┃    ┃
  rc.d   cron.d X11R6  src   lib   local    man  bin
  ┃
  ┏━━━┳━━┳━┻━┳━━━┓
  ┃      ┃    ┃      ┃      ┃
  init.d rc0.d  rc1.d  rc2.d …… linux bin lib src
  Linux采用的是树型结构。最上层是根目录,其他的所有目录都是从根目录出发而生成的。微软的DOS和windows也是采用树型结构,但是在DOS和 windows中这样的树型结构的根是磁盘分区的盘符,有几个分区就有几个树型结构,他们之间的关系是并列的。但是在linux中,无论操作系统管理几个磁盘分区,这样的目录树只有一个。从结构上讲,各个磁盘分区上的树型目录不一定是并列的。
  如果这样讲不好理解的话,我来举个例子:
  有一块硬盘,分成了4个分区,分别是/;/boot;/usr和windows下的fat
  对于/和/boot或者/和/usr,它们是从属关系;对于/boot和/usr,它们是并列关系。
  如果我把windows下的fat分区挂载到/mnt/winc下,(挂载??哦,别急,呵呵,一会就讲,一会就讲。)那么对于/mnt/winc和/usr或/mnt/winc和/boot来说,它们是从属于目录树上没有任何关系的两个分支。
  因为linux是一个多用户系统,制定一个固定的目录规划有助于对系统文件和不同的用户文件进行统一管理。但就是这一点让很多从windows转到linux的初学者感到头疼。下面列出了linux下一些主要目录的功用。
/bin 二进制可执行命令
/dev 设备特殊文件
/etc 系统管理和配置文件
/etc/rc.d 启动的配置文件和脚本
/home 用户主目录的基点,比如用户user的主目录就是/home/user,可以用~user表示
/lib 标准程序设计库,又叫动态链接共享库,作用类似windows里的.dll文件
/sbin 系统管理命令,这里存放的是系统管理员使用的管理程序
/tmp 公用的临时文件存储点
/root 系统管理员的主目录(呵呵,特权阶级)
/mnt 系统提供这个目录是让用户临时挂载其他的文件系统。
/lost+found 这个目录平时是空的,系统非正常关机而留下“无家可归”的文件(windows下叫什么.chk)就在这里
/proc 虚拟的目录,是系统内存的映射。可直接访问这个目录来获取系统信  息。
/var 某些大文件的溢出区,比方说各种服务的日志文件
/usr 最庞大的目录,要用到的应用程序和文件几乎都在这个目录。其中包  含:
/usr/X11R6 存放X window的目录
/usr/bin 众多的应用程序
/usr/sbin 超级用户的一些管理程序
/usr/doc linux文档
/usr/include linux下开发和编译应用程序所需要的头文件
/usr/lib 常用的动态链接库和软件包的配置文件
/usr/man 帮助文档
/usr/src 源代码,linux内核的源代码就放在/usr/src/linux里
/usr/local/bin 本地增加的命令
/usr/local/lib 本地增加的库



 二 、linux文件系统
  文件系统指文件存在的物理空间,linux系统中每个分区都是一个文件系统,都有自己的目录层次结构。linux会将这些分属不同分区的、单独的文件系统按一定的方式形成一个系统的总的目录层次结构。一个操作系统的运行离不开对文件的操作,因此必然要拥有并维护自己的文件系统。
  Llinux文件系统使用索引节点来记录文件信息,作用像windows的文件分配表。
  索引节点是一个结构,它包含了一个文件的长度、创建及修改时间、权限、所属关系、磁盘中的位置等信息。一个文件系统维护了一个索引节点的数组,每个文件或目录都与索引节点数组中的唯一一个元素对应。系统给每个索引节点分配了一个号码,也就是该节点在数组中的索引号,称为索引节点号。
  linux文件系统将文件索引节点号和文件名同时保存在目录中。所以,目录只是将文件的名称和它的索引节点号结合在一起的一张表,目录中每一对文件名称和索引节点号称为一个连接。
  对于一个文件来说有唯一的索引节点号与之对应,对于一个索引节点号,却可以有多个文件名与之对应。因此,在磁盘上的同一个文件可以通过不同的路径去访问它。
  可以用ln命令对一个已经存在的文件再建立一个新的连接,而不复制文件的内容。连接有软连接和硬连接之分,软连接又叫符号连接。它们各自的特点是:
  硬连接:原文件名和连接文件名都指向相同的物理地址。
  目录不能有硬连接;硬连接不能跨越文件系统(不能跨越不同的分区)
  文件在磁盘中只有一个拷贝,节省硬盘空间;
  由于删除文件要在同一个索引节点属于唯一的连接时才能成功,因此可以防止不必要的误删除。
  符号连接:用ln -s命令建立文件的符号连接符号连接是linux特殊文件的一种,作为一个文件,它的数据是它所连接的文件的路径名。类似windows下的快捷方式。
  可以删除原有的文件而保存连接文件,没有防止误删除功能。
  这一段的的内容过于抽象,又是节点又是数组的,我已经尽量通俗再通俗了,又不好加例子作演示。大家如果还是云里雾里的话,我也没有什么办法了,只有先记住,日后在实际应用中慢慢体会、理解了。这也是我学习的一个方法吧。
  三 、挂载文件系统
  由上一节知道,linux系统中每个分区都是一个文件系统,都有自己的目录层次结构。linux会将这些分属不同分区的、单独的文件系统按一定的方式形成一个系统的总的目录层次结构。这里所说的“按一定方式”就是指的挂载。
  将一个文件系统的顶层目录挂到另一个文件系统的子目录上,使它们成为一个整体,称为挂载。把该子目录称为挂载点。
  举个例子吧:
  根分区:
  /根目录
  ┃
  ┏━━━━┳━━━━━┳━━━━━┳━━━━━╋━━━━━┳━━━━━┳━━━━━┳━━━━━┓
  ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃
  bin home dev etc lib sbin tmp usr var
  ┃
  ┏━┻━┓
  ┃ ┃
  rc.d cron.d
  ┃
  ┏━━━┳━━━┳━┻━┳━━━━┓
  ┃ ┃ ┃ ┃ ┃
  init.d rc0.d rc1.d rc2.d ……
  /usr分区 :
  usr
  ┃
  ┏━━━━┳━━━╋━━━┳━━━┳━━━┓
  ┃ ┃ ┃ ┃ ┃ ┃
  X11R6 src lib local man bin
  ┃ ┃
  ┃ ┏━━━╋━━━┓
  ┃ ┃ ┃ ┃
  linux bin lib src
  挂载之后就形成了文章开始时的那个图。像不像挂上去的?
  注意:1、挂载点必须是一个目录。
  2、一个分区挂载在一个已存在的目录上,这个目录可以不为空,但挂载后这个目录下以前的内容将不可用。
  对于其他操作系统建立的文件系统的挂载也是这样。但是需要理解的是:光盘、软盘、其他操作系统使用的文件系统的格式与linux使用的文件系统格式是不一样的。光盘是ISO9660;软盘是fat16或ext2;windows NT是fat16、NTFS;windows98是fat16、fat32;windows2000和windowsXP是fat16、fat32、 NTFS。挂载前要了解linux是否支持所要挂载的文件系统格式。
挂载时使用mount命令:
  格式:mount [-参数] [设备名称] [挂载点]
  其中常用的参数有
  -t 指定设备的文件系统类型,常见的有:
  minix linux最早使用的文件系统
  ext2 linux目前常用的文件系统
  msdos MS-DOS的fat,就是fat16
  vfat windows98常用的fat32
  nfs 网络文件系统
  iso9660 CD-ROM光盘标准文件系统
  ntfs windows NT 2000的文件系统
  hpfs OS/2文件系统
  auto 自动检测文件系统
  -o 指定挂载文件系统时的选项。有些也可用在/etc/fstab中。常用的  有
  codepage=XXX 代码页
  iocharset=XXX 字符集
  ro 以只读方式挂载
  rw 以读写方式挂载
  nouser 使一般用户无法挂载
  user 可以让一般用户挂载设备
  提醒一下,mount命令没有建立挂载点的功能,因此你应该确保执行mount命令时,挂载点已经存在。(不懂?说白了点就是你要把文件系统挂载到哪,首先要先建上个目录。这样OK?)
  例子:windows98装在hda1分区,同时计算机上还有软盘和光盘需要挂载。
# mk /mnt/winc
# mk /mnt/floppy
# mk /mnt/cdrom
# mount -t vfat /dev/hda1 /mnt/winc
# mount -t msdos /dev/fd0 /mnt/floppy
# mount -t iso9660 /dev/cdrom /mnt/cdrom
  现在就可以进入/mnt/winc等目录读写这些文件系统了。
  要保证最后两行的命令不出错,要确保软驱和光驱里有盘。(要是硬盘的磁盘片也可以经常随时更换的话,我想就不会犯这样的错误了 :-> )
  如果你的windows98目录里有中文文件名,使用上面的命令挂载后,显示的是一堆乱码。这就要用到 -o 参数里的codepage iocharset选项。codepage指定文件系统的代码页,简体中文中文代码是936;iocharset指定字符集,简体中文一般用cp936或 gb2312。
  当挂载的文件系统linux不支持时,mount一定报错,如windows2000的ntfs文件系统。可以重新编译linux内核以获得对该文件系统的支持。关于重新编译linux内核,就不在这里说了。
  四 、自动挂载
  每次开机访问windows分区都要运行mount命令显然太烦琐,为什么访问其他的linux分区不用使用mount命令呢?
  其实,每次开机时,linux自动将需要挂载的linux分区挂载上了。那么我们是不是可以设定让linux在启动的时候也挂载我们希望挂载的分区,如windows分区,以实现文件系统的自动挂载呢?
  这是完全可以的。在/etc目录下有个fstab文件,它里面列出了linux开机时自动挂载的文件系统的列表。我的/etc/fstab文件如下:
/dev/hda2 / ext3 defaults 1 1
/dev/hda1 /boot ext3 defaults 1 2
none /dev/pts devpts gid=5,mode=620 0 0
none /proc proc defaults 0 0
none /dev/shm tmpfs defaults 0 0
/dev/hda3 swap swap defaults 0 0
/dev/cdrom /mnt/cdrom iso9660 noauto,codepage=936,iocharset=gb2312 0 0
/dev/fd0 /mnt/floppy auto noauto,owner,kudzu 0 0
/dev/hdb1 /mnt/winc vfat defaults,codepage=936,iocharset=cp936 0 0
/dev/hda5 /mnt/wind vfat defaults,codepage=936,iocharset=cp936 0 0
  在/etc/fstab文件里,第一列是挂载的文件系统的设备名,第二列是挂载点,第三列是挂载的文件系统类型,第四列是挂载的选项,选项间用逗号分隔。第五六列不知道是什么意思,还望高手指点。
  在最后两行是我手工添加的windows下的C;D盘,加了codepage=936和iocharset=cp936参数以支持中文文件名。参数defaults实际上包含了一组默认参数:
  rw 以可读写模式挂载
  suid 开启用户ID和群组ID设置位
  dev 可解读文件系统上的字符或区块设备
  exec 可执行二进制文件
  auto 自动挂载
  nouser 使一般用户无法挂载
  async 以非同步方式执行文件系统的输入输出操作
  大家可以看到在这个列表里,光驱和软驱是不自动挂载的,参数设置为noauto。(如果你非要设成自动挂载,你要确保每次开机时你的光驱和软驱里都要有盘,呵呵。)

posted @ 2013-10-22 09:44 顺其自然EVO 阅读(191) | 评论 (0)编辑 收藏

Selenium2.0功能测试之你所不知道的sendKeys

 其实光看字面上的理解sendKeys这个api感觉只有输入字符这一个功能,其实这样理解是错误的其实这个api的真正作用是模拟键盘的操作(包含了输入字符),不过输入字符是其主要的功能,下面介绍一下其他的用法:
  这里就直接上代码了
package org.coderinfo.demo;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class ApiSendKeys {
private static final String URL = "file:///C:/user/Desktop/Selenium/operate.html";
public static void main(String[] args) {
WebDriver driver = new ChromeDriver(); // create a chrome driver
driver.manage().window().maximize(); // max size the chrome window
driver.get(URL); // open URL with the chrome browser
try {
Thread.sleep(2000);  // wait for page loading
} catch (InterruptedException e) {
e.printStackTrace();
}
driver.findElement(By.id("UserName")).sendKeys("coderinfo");  // Get input element and input some words
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
driver.findElement(By.id("UserName")).sendKeys(Keys.chord(Keys.CONTROL + "a")); // Use sendKeys to select all the content
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
driver.findElement(By.id("UserName")).sendKeys(Keys.chord(Keys.CONTROL + "x")); // Use sendKeys to cut the content
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
driver.findElement(By.id("UserEmail")).sendKeys(Keys.chord(Keys.CONTROL + "v"));   // Use sendKeys to copy the content
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
driver.quit();  // close webdriver
}
}



  相信如何模拟其他的快捷键就不用我一一列举了,我也没有试过太多,因为我就用过一次。
  这里是测试的页面operate.html的源码:
<!DOCTYPE html>
<html>
<head>
<title>Operate Element</title>
</head>
<body>
<h3>Operate Element</h3>
<form class="form-h">
<input type="text" class="in" id="UserName" /><br />
<input type="text" class="in" id="UserEmail" /><br />
<input type="submit" class="in" />
<input type="reset" class="in" />
</form>
</body>
</html>
相关文章:
Selenium2.0功能测试之Alert/Confirm/Prompt的处理

posted @ 2013-10-21 10:36 顺其自然EVO 阅读(2180) | 评论 (0)编辑 收藏

生产者消费者问题理解与Java实现

 生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
  要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号灯法等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。
  Java代码模拟生产者-消费者
  产品类
package org.dennist.thread.demo;
/**
*
*  Product.java
*
*  @version : 1.1
*
*  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
*
*  @since     : 1.0        创建时间:    2013-2-25        上午09:03:38
*
*  TODO     :    class Product.java is used for ...
*
*/
public class Product {    //产品类
private int productId = 0;
public Product(int productId){
this.productId = productId;
}
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
@Override
public String toString() {
return ""+productId;
}
}

 仓库类
package org.dennist.thread.demo;
/**
*
*  StoreHouse.java
*
*  @version : 1.1
*
*  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
*
*  @since     : 1.0        创建时间:    2013-2-25        上午08:55:33
*
*  TODO     :    仓库
*
*/
public class StoreHouse {
private int base = 0;
private int top = 0;
//仓库大小
private Product[] products = new Product[10];
/**
* 生产产品
* @param product
*/
public synchronized void push(Product product){
if(top==products.length){    //如果仓库已满,等待消费
try {
System.out.println("仓库已满,正在等待消费..");
wait();
}catch (InterruptedException e) {
System.out.println("stop push product because other reasons");
}
}
//仓库未满,将生产的产品入库
products[top] = product;
//库中产品数量+1
top++;
}
/**
* 消费产品
* @return
*/
public synchronized Product pop() {
Product product = null;
while (top == base) {     //仓库未空,不能消费
notify();
try {
System.out.println("仓库已空,正等待生产...");
wait();
} catch (InterruptedException e) {
System.out.println("stop push product because other reasons");
}
}
//仓库未空,等待消费
top--;
product = products[top];
products[top] = null;
return product;
}
}
  生产者类
package org.dennist.thread.demo;
/**
*
*  Producer.java
*
*  @version : 1.1
*
*  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
*
*  @since     : 1.0        创建时间:    2013-2-25        上午08:53:16
*
*  TODO     :    生产者
*
*/
public class Producer implements Runnable{
private String producerName ;
private StoreHouse storeHouse ;
public Producer(String producerName, StoreHouse storeHouse) {
this.producerName = producerName;
this.storeHouse = storeHouse;
}
public void setProducerName(String producerName) {
this.producerName = producerName;
}
public String getProducerName() {
return producerName;
}
@Override
public void run() {
execProcuct();
}
private void execProcuct() {
int i = 0;
while(true){
i++;
Product pro = new Product(i);
storeHouse.push(pro);
System.out.println(getProducerName() + " 生产了 " + pro);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
return;
}
}
}
}  消费者类
package org.dennist.thread.demo;
/**
*
*  Consumer.java
*
*  @version : 1.1
*
*  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
*
*  @since     : 1.0        创建时间:    2013-2-25        上午08:53:47
*
*  TODO     :    消费者
*
*/
public class Consumer implements Runnable{
private String consumerName = null;
private StoreHouse storeHouse = null;
public Consumer(String consumerName, StoreHouse storeHouse) {
this.consumerName = consumerName;
this.storeHouse = storeHouse;
}
public void setConsumerName(String consumerName) {
this.consumerName = consumerName;
}
public String getConsumerName() {
return consumerName;
}
public void execConsume() {
while (true) {
System.out.println(getConsumerName() + " 消费了 " + storeHouse.pop());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
return;
}
}
}
@Override
public void run() {
execConsume();
}
}
  测试主类
package org.dennist.thread.demo;
/**
*
*  TestPC.java
*
*  @version : 1.1
*
*  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
*
*  @since     : 1.0        创建时间:    2013-2-25        上午09:18:52
*
*  TODO     :    生产者消费者模拟
*
*/
public class TestPC {
public static void main(String[] args) {
StoreHouse storeHouse = new StoreHouse();
Producer producer = new Producer("生产者", storeHouse);
Consumer comsumer = new Consumer("消费者", storeHouse);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(comsumer);
t1.start();
t2.start();
}
}
关于JAVA多线程同步
  JAVA多线程同步主要依赖于若干方法和关键字
  1 wait方法:
  该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所在的代码块的锁,并在其他线程调用notify或者notifyAll方法时恢复到竞争锁状态(一旦获得锁就恢复执行)。
  调用wait方法需要注意几点:
  第一点:wait被调用的时候必须在拥有锁(即synchronized修饰的)的代码块中。
  第二点:恢复执行后,从wait的下一条语句开始执行,因而wait方法总是应当在while循环中调用,以免出现恢复执行后继续执行的条件不满足却继续执行的情况。
  第三点:若wait方法参数中带时间,则除了notify和notifyAll被调用能激活处于wait状态(等待状态)的线程进入锁竞争外,在其他线程中interrupt它或者参数时间到了之后,该线程也将被激活到竞争状态。
  第四点:wait方法被调用的线程必须获得之前执行到wait时释放掉的锁重新获得才能够恢复执行。
  2 notify方法和notifyAll方法:
  notify方法通知调用了wait方法,但是尚未激活的一个线程进入线程调度队列(即进入锁竞争),注意不是立即执行。并且具体是哪一个线程不能保证。另外一点就是被唤醒的这个线程一定是在等待wait所释放的锁。
  notifyAll方法则唤醒所有调用了wait方法,尚未激活的进程进入竞争队列。
  3 synchronized关键字:
  第一点:synchronized用来标识一个普通方法时,表示一个线程要执行该方法,必须取得该方法所在的对象的锁。
  第二点:synchronized用来标识一个静态方法时,表示一个线程要执行该方法,必须获得该方法所在的类的类锁。
  第三点:synchronized修饰一个代码块。类似这样:synchronized(obj) { //code.... }。表示一个线程要执行该代码块,必须获得obj的锁。这样做的目的是减小锁的粒度,保证当不同块所需的锁不冲突时不用对整个对象加锁。利用零长度的byte数组对象做obj非常经济。
  4 atomic action(原子操作):
  在JAVA中,以下两点操作是原子操作。但是c和c++中并不如此。
  第一点:对引用变量和除了long和double之外的原始数据类型变量进行读写。
  第二点:对所有声明为volatile的变量(包括long和double)的读写。
  另外:在java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依赖于同步机制的线程安全的类和方法。
  附录:
  进程间通信
  进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法。线程是计算机系统分配资源的最小单位。每个进程都有自己的一部分独立的系统资源,彼此是隔离的。为了能使不同的进程互相访问资源并进行协调工作,才有了进程间通信。这些进程可以运行在同一计算机上或网络连接的不同计算机上。
  进程间通信技术包括消息传递、同步、共享内存和远程过程调用。IPC是一种标准的Unix通信机制。
  主要的IPC方法有
  (1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
  (2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
  (3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。
  (4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
  (5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
  (6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
  (7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
  (8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

posted @ 2013-10-21 10:29 顺其自然EVO 阅读(201) | 评论 (0)编辑 收藏

Oracle 跨操作系统 迁移 说明

  关于跨OS 的DB 迁移,MOS 上有相关的文章说明:[ID733205.1]。
  To migrate anexisting Oracle database (NOT BINARIES)  from one Operating Systemplatform to another (i.e. Windows to Solaris) ,This can occur as part ofan Oracle version upgrade (Oracle 8i .. Oracle 11G) or within the sameOracle version: (Oracle 10.2 to Oracle 10.2).
  --可以将Oracle DB 从一个操作系统迁移到另一个操作系统,比如从windows到Solaris,注意这里的迁移仅仅是数据的迁移,不包含DB 的安装介质。这个迁移可以是不同DB版本的迁移,比如从8i 到11g,也可以是相同版本的迁移,比如从10.2 到10.2.
  Changes withinan Operating System (ie: Linux,Windows or Solaris from 32 bit to 64 bit) arenot considered cross platform migrations and are performed as normal versionupgrades/wordsize conversions.
  --改变操作系统不用考虑系统的平台,可以按照正常的db version upgrades 和 wordsize 的改变来操作。
  一. 解决方法
  There is no migrationutility (Script or DBUA) to perform a cross platform migration of an OracleDatabase.
  --没有迁移工具如脚本或者DBUA来执行跨OS的数据迁移。
  Changingplatforms requires the database be re-built and / or the data moved using oneof the following methods:
  --跨平台的迁移需要重建数据库,然后使用如下的一种方法来完成数据的迁移工作
  (1)    Export / Import to include theuse of Datapump facilities. All versions support Export/Import but for Datapump10.1.0.2 or higher is required
  (2)    Transportable Tablespaces 10Gor Later
  (3)    RMAN Convert Databasefunctions. 10G or Later
  (4)    Streams Replication
  (5)    Create Table As Select (CTAS)
  (6)    Dataguard Heterogeneous Primaryand Physical Standbys
  (7)    Oracle Golden Gate
  Each availablechoice will have strengths and limitations to include data types, time requiredand potential costs.
  --每一个可选的方法都有它的优势和限制,如数据类型,需要的时间和一些潜在的消耗。
  The choicesavailable will depend on BOTH the Operating System and Oracle versions on boththe source and destination.
  --方法是否可用也取决与Source 和 Destination 两端的操作系统和Oracle版本。
  二. 示例
  There areplatform limitations when using Dataguard Heterogeneous Primary and PhysicalStandbys。
  --比如使用DG的异构平台来迁移时,就会有操作系统的限制。 关于这块内容,之前有详细的Blog:
  Oracle DataGuard 支持的异构平台 说明
  http://blog.csdn.net/tianlesoftware/article/details/7241488
  RMAN ConvertDatabase only works if both source and destination belong to the same ENDIANformat.
  --RMAN Convert DB 仅在source 和destination 的ENDIAN 格式相同的情况下才可以使用。
  RMAN's convertfunction for Transportable Tablespaces will convert from one ENDIAN format toanother.
  在ENDIAN 格式不同的情况下,可以使用RMAN convert function 来转换ENDIAN 从一种格式到另一种格式。如:
  RMAN> convert tablespace TBS1 to platform="Linux IA(32-bit)" FORMAT '/tmp/%U';
  两端相同之后,就可以进行Transportabletablespace 的操作。
  可以通过v$transportable_platform视图查看系统的ENDIAN 格式:
SQL> columnplatform_name format a35
SQL> select *from v$transportable_platform order by 1;
PLATFORM_IDPLATFORM_NAME                      ENDIAN_FORMAT
---------------------------------------------- --------------
1 Solaris[tm] OE (32-bit)             Big
2 Solaris[tm] OE (64-bit)             Big
3 HP-UX (64-bit)                      Big
4 HP-UX IA (64-bit)                   Big
5 HP Tru64 UNIX                       Little
6 AIX-Based Systems (64-bit)          Big
7 Microsoft Windows IA (32-bit)       Little
8 Microsoft Windows IA (64-bit)       Little
IBM zSeries Based Linux             Big
10 Linux IA (32-bit)                   Little
11 Linux IA (64-bit)                   Little
12 Microsoft Windows x86 64-bit        Little
13 Linux x86 64-bit                    Little
15 HP Open VMS                         Little
16 Apple Mac OS                        Big
17 Solaris Operating System (x86)      Little
18 IBM Power Based Linux               Big
19 HP IA Open VMS                      Little
20 Solaris Operating System(x86-64)   Little
21 Apple Mac OS (x86-64)               Little
20 rowsselected.

posted @ 2013-10-21 10:26 顺其自然EVO 阅读(432) | 评论 (0)编辑 收藏

Selenium2.0功能测试之Alert/Confirm/Prompt的处理

 WebDriver中处理原生JS的 alert confirm 以及prompt是很方便的(虽然现在原生JS的实现方式用的很少了)。具体思路是使用switchTo.alert()方法定位到当前的alert/confirm/prompt(这里注意当前页面只能同时含有一个控件,如果多了会报错的,所以这就需要一一处理了),然后在调用Alert的方法进行操作,Alert提供了以下几个方法:
  getText : 返回alert/confirm/prompt中的文字内容
  accept : 点击确认按钮
  dismiss : 点击取消按钮如果有取消按钮的话
  sendKeys : 向prompt中输入文字    //这个方法在chromedriver中不起作用,IE的话由于家中无Windows没有做demo.
package org.coderinfo.demo;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class AlertDemo {
private static final String URL = "file:///home/moon/Desktop/alert_demo.html";
/**
* @author CoderInfo
*/
public static void main(String[] args) throws InterruptedException {
WebDriver driver = new FirefoxDriver();  //创建一个firefox的 webdriver
driver.get(URL);
driver.manage().window().maximize();
Thread.sleep(1000);
// 点击弹出alert
driver.findElement(By.id("alert")).click();
Thread.sleep(3000);
Alert alert = driver.switchTo().alert(); //捕获alert
alert.accept();  //点击确认按钮
Thread.sleep(3000);  //等待3s
//点击弹出confirm
driver.findElement(By.id("confirm")).click();
Thread.sleep(3000);
Alert confirm = driver.switchTo().alert();  //捕获confirm
String confirmText = confirm.getText(); //获取confirm中的文字信息
System.out.println(confirmText);
confirm.accept();  //confirm 点击确认按钮
//      confirm.dismiss();  //confirm点击取消按钮
Thread.sleep(3000);
//点击弹出prompt
driver.findElement(By.id("prompt")).click();
Thread.sleep(3000);
Alert prompt = driver.switchTo().alert();  //捕获prompt
//      String promptText = prompt.getText(); //获取prompt中的文字信息
//      System.out.println(promptText);
prompt.sendKeys("可能是由于太懒了");  //向prompt中输入内容
Thread.sleep(3000);
prompt.accept();  //prompt 点击确认按钮
//      prompt.dismiss();  //prompt点击取消按钮
Thread.sleep(3000);
driver.quit(); // close webdriver
}
}

 下面是测试页面alert_demo.html源代码
<html>
<head>
<title>Alert</title>
<script type="text/javascript">
function testAlert(){
alert("测试Alert");
}
function testConfirm(){
confirm("你喜欢自动化测试吗?");
}
function testPrompt(){
var content = prompt("你为什么喜欢自动化?");
document.write(content);
}
</script>
</head>
<body>
<h2>Test Alert</h2>
<input type="button" value="alert" onclick="testAlert()" id="alert"/>
<input type="button" value="confirm" onclick="testConfirm()" id="confirm"/>
<input type="button" value="prompt" onclick="testPrompt()" id="prompt"/>
</body>
</html>
相关文章:
Selenium2.0功能测试之Start browsers

posted @ 2013-10-18 11:37 顺其自然EVO 阅读(974) | 评论 (0)编辑 收藏

Java 条件编译 Conditional Compilation

 根据Java编译器的优化的机制,Java也能够提供条件编译。对于条件设为false的语句,编译器将不对条件覆盖的代码段生成字节码。
  不仅可以用简单的boolean常量值来做条件编译,还可以用字符串常量及任何其他类型的常量.
  例如:
  1. 简单的boolean常量。
final
boolean
isDebug
=
true;
if(isDebug)
{
//debug模式状态
System.out.println("现在是debug模式!");
}
  2. 字符串常量。
  这里要注意的是不能用equals方法,必须用简单的判断符号(==, != ....)
final
String
debug
=
"true";
final
String
currentMode
=
debug;
if(currentMode
==
debug)
{
//debug模式状态
System.out.println("现在是debug模式!");
}


3. 其他常量就不设置了
  当然我们可以写个CompilationConfig类,里面仅设置所有要用条件编译的选项。
  这里我写了个示例,呵呵,记录下以后不要忘记了,也希望能够帮助需要这方向东东的人。
  ConmpilationCofig.java
package
com.litefeel.javaConditionalCompilationTest;
/**
* 这是一个配置条件编译的类,实际编译后这个类并不跟其他文件关联
* @author lite3
*
*/
public
class
CompilationConfig
{
//配置是否是debug模式, 关键字 final是启用条件编译的关键
static
public
final
boolean
isDebug
=
true;
//用条件编译来判别不同的平台
static
public
final
String
platform1
=
"platfor1";
static
public
final
String
platform2
=
"platform2";
//当前所用的平台
static
public
final
String
currentPlatform
=
platform1;
}
  javaConditionalCompilationTest.java
package
com.litefeel.javaConditionalCompilationTest;
public
class
ConditionalCompilationTest
{
public
ConditionalCompilationTest()
{
//用条件编译来去除多余代码
//这里仅仅是输出语句,也可以是其他逻辑代码
final
String
debug
=
"true";
final
String
currentMode
=
debug;
if(currentMode
==
debug)
{
//debug模式状态
System.out.println("现在是debug模式!");
}
//用条件编译来判别不同的平台
//平台1
if(CompilationConfig.currentPlatform
==
CompilationConfig.platform1)
{
System.out.println("这里是平台1的输出!");
}
//平台2
if(CompilationConfig.currentPlatform
==
CompilationConfig.platform2)
{
System.out.println("这里是平台2的输出!");
}
}
static
public
void
main(String[]
args)
{
//实例化条件编译
new
ConditionalCompilationTest();
}
}



编译后,javaConditionalCompilationTest.class 反编译后为:
package
com.litefeel.javaConditionalCompilationTest;
import
java.io.PrintStream;
public
class
ConditionalCompilationTest
{
public
ConditionalCompilationTest()
{
String
debug
=
"true";
String
currentMode
=
"true";
System.out.println("现在是debug模式!");
System.out.println("这里是平台1的输出!");
}
public
static
void
main(String[]
args)
{
new
ConditionalCompilationTest();
}
}
  很明显已经去掉了平台2的代码。
  如果不知道怎么反编译,请使用Java 反编译工具反编译就行了。

posted @ 2013-10-18 11:36 顺其自然EVO 阅读(525) | 评论 (0)编辑 收藏

memcached性能测试 twemperf

 twemperf 是一个用来测试 memcached 服务器性能的工具。
  测试实例:
$ mcperf --linger=0 --timeout=5 --conn-rate=1000 --call-rate=1000 --num-calls=10 --num-conns=1000 --sizes=u1,16
Total: connections 1000 requests 10000 responses 10000 test-duration 1.009 s
Connection rate: 991.1 conn/s (1.0 ms/conn <= 23 concurrent connections)
Connection time [ms]: avg 10.3 min 10.1 max 14.1 stddev 0.1
Connect time [ms]: avg 0.2 min 0.1 max 0.8 stddev 0.0
Request rate: 9910.5 req/s (0.1 ms/req)
Request size [B]: avg 35.9 min 28.0 max 44.0 stddev 4.8
Response rate: 9910.5 rsp/s (0.1 ms/rsp)
Response size [B]: avg 8.0 min 8.0 max 8.0 stddev 0.0
Response time [ms]: avg 0.2 min 0.1 max 13.4 stddev 0.00
Response time [ms]: p25 1.0 p50 1.0 p75 1.0
Response time [ms]: p95 1.0 p99 1.0 p999 1.0
Response type: stored 10000 not_stored 0 exists 0 not_found 0
Response type: num 0 deleted 0 end 0 value 0
Response type: error 0 client_error 0 server_error 0
Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
Errors: fd-unavail 0 ftab-full 0 addrunavail 0 other 0
CPU time [s]: user 0.64 system 0.35 (user 63.6% system 35.1% total 98.7%)
Net I/O: bytes 428.7 KB rate 424.8 KB/s (3.5*10^6 bps)

posted @ 2013-10-18 11:36 顺其自然EVO 阅读(540) | 评论 (0)编辑 收藏

数据库系统原理之关系运算

 关系的基本运算只要分为两类,第一类是传统的集合操作:并、交、差、笛卡尔积(乘法)、笛卡尔积的逆运算(除法)。第二类是扩充的关系操作:投影(对关系的垂直分割)、选择(对关系的水平分割)、连接和自然连接(关系的结合)。
  五个基本操作:
  举例说明:
  两个关系如下:
  并:
  差:
  笛卡尔积:
  选择:

投影:
  四个组合操作:
  举例说明:
  交:
  引用上述的两个关系R和S,则RnS为:


posted @ 2013-10-18 11:32 顺其自然EVO 阅读(220) | 评论 (0)编辑 收藏

LR与性能测试分享

 目前我工作在 一个专门给银行做核心系统的公司,主要负责自动化测试性能方向工作,也负责员工培训和测试团队的一些技术提高工作,CS架构的应用程序比较多 ,大部分系统构建在AIX 和linux系统上,今天主要和大家探讨loadrunner工具在性能测试方面的一些应用。
  说来惭愧  性能测试也走过很多弯路,以为弄明白 lr 就天下无敌了,其实对很多性能测试基本问题大部分都想当然,所以我对新手的建议还是从基础开始。从性能测试的基本概念到各种指标的了解对网络、硬件、工具、方法论从实践的角度 不断提高自己
  才能有机会走入性能测试这道门,换个角度考虑性能测试需要的素养和知识储备也是全方位的。就拿lr来说,装lr 这么简单的事我就装了不下几十遍失败的多 成功的少。从不看系统给的提示和安装指南,新手很容易畏难或者不知所以,lr支持的协议众多,更多的人倒在了协议选择上。
  lr的脚本需要有一定的编程基础,你至少应该会c 或者 Java 或者其他lr支持的脚本语言一种或者几种,而且你会发现 lr里支持的脚本和纯c 、纯java ,但又不太一样,当然,刚开始时,如果你能简单修改脚本就不错了。我记得我刚开始接触lr的时候没有人可以问,只有死扣lr的文档,文档都是英文的中文的很少。有人也许会问:弄lr 非要汉化版 否则不会用 我这里还是奉劝大家 尽量使用英文版。
  原因有二
  1.lr的汉化一般都不彻底 而且汉化后引起的概念歧义 会让你使用起来更加困难;
  2.lr的新版出来 汉化版 不会那么快, 时间上来说 接触英文版 要比 汉化版 要快, 这样了解 lr的速率也比 等着 汉化版 出来 再使用要快,当然还好 lr已经提供了 比较丰富的 帮助文档,甚至还有视频教程, 这点 很多 学习lr的人 都应该清楚。
  F1 可以查看 lr中的函数, 但是你知道 这些函数的文档 都在哪里?
   我觉得 lr的学习, 应该从以下几个方面进行
  1.lr的安装 lr各个组件之间的关系
  1.1 controller
  1.2Vugen Generator
  1.3Analysis
  当然新版里还有Launcher
  2.lr的协议的详解
  这个文档我已经共享在群共享里了,
  至于协议的选择需要结合具体的应用程序数据库来选择了。
  3.lr的工作流程
  这张图 已经很说明问题了
  3.1 制定测试方案
  兵马未动粮草先行,性能测试不同于功能测试, 这里的测试方案和性能测试计划有所不同,方案侧重策略的选择,计划侧重时间人员分配、 阶段划分 等一些具体的内容制定的做法,当然方案的前提是对系统的了解。
3.2 Vugen
  众所周知,lr可以通过录制的方式来单用户的脚本事务是脚本的基础
  一方面 事务区分了我们关注的性能测试点
  另一方面,事务是性能指标 ,分析的基础事务还和以后的集合点策略紧密关联大家可能把关注点更多放在脚本的录制上面
  录制时候的各项设定也是让大家头疼的一个东西,不过lr仅仅是一个压力负载的工具。大局上我们要明确你性能测试的目标是什么?
  否则你都不知道 为啥要加参数化 何处加集合点如何确认事务失败?事务划分的粒度默认lr认为一个action就是一个事务换个角度 考虑 你划分的事务是有意义的脚本编写是一个内功
  你需要从熟悉各个函数开始
  lr的API还是很丰富的 既有函数声明解释还有实例
  建议大家可以通过一些简单的脚本来熟悉lr的脚本编写
  3.设计场景
  需要注意的是lr的每一个工作流程的前提都是前一个步骤执行成功
  3.1 场景选择
  3.2 场景设置
  4.就是运行该场景了
  这里也要在明确测试目标的前提下设计一个或多个不同的场景来执行,运行过程中可能会出现的各种问题,要析和解决。如果要做到无人值守,那对脚本、场景要求都会比较高 ,脚本的容错性、场景的设置 都需要考虑当然 系统的监控 也是必不可少的重要指标
  5.最后就是对lr生成的测试报告进行分析以上,只是lr基本的工作流程任何一个拿出来都可以讲个专题以后有时间给大家继续分享。

posted @ 2013-10-17 11:41 顺其自然EVO 阅读(385) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 193 194 195 196 197 198 199 200 201 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜