qileilove

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

Linux架设时间服务器

 ntpdate 0.centos.pool.ntp.org
  在linux下,我们可以通过自带的NTP(Network Time Protocol)协议通过网络使自己的系统保持精确的时间。
  可用的公共时间服务器列表可以从下面的地址获取:
  http://ntp.isc.org/bin/view/Servers/NTPPoolServers
  NTP是用来使系统和一个精确的时间源保持时间同步的协议。建议大家在自己管理的网络中建立至少一台时间服务器来同步本地时间,这样可以使得在不同的系统上处理和收集日志和管理更加容易。
  介绍一下环境:179为本地时间服务器,其他服务器和179同步。179和网上时间服务器同步。
  一、配置179时间服务器
  1、首先查询NTP软件版本
  rpm -qa | grep ntp
  ntp-4.1.2-4.EL3.1
  如果没有可以从linux安装盘上查找,安装此ntp包
  2、编辑配置文件
  vi /etc/ntp.conf
  首先定义服务器
  server pool.ntp.org
  restrict default nomodify notrap noquery
  restrict 192.168.0.0 mask 255.255.255.0 notrust nomodify notrap #从192.168.0.1-192.168.0.254的服务器都可以使用我们的NTP服务器来同步时间
  注释掉以下一行
  #restrict default ignore
  3、启动NTP服务器
  #chkconfig ntpd on
  #/etc/init.d/ntpd start
  #/etc/init.d/ntpd stop
  #/etc/init.d/ntpd restart
  默认情况下,我们配置的NTP服务器不会去时间源那里同步时间,所以必须修改/etc/ntp/step-tickers文件,加入我们的时间源,这样每次通过/etc/init.d/ntpd 来启动服务的时候就会自动更新时间了
  检查服务器同步状态:
  ntpq -p
  ntptrace 192.168.0.179
  如果输出正确,则说明时间服务器成功。每次启动服务器,会自动同步时间。

 二、配置LINUX客户端
  在linux客户端上执行
  ntpdate ntp_server_ip
  就可以根据时间服务器统一局域网的时间了,将上面的命令放到cron里每天早上3点定期执行
  crontab –e
  然后输入
  0 3 * * * /usr/sbin/ntpdate 192.168.0.179
  下载安装包:
  1、编译安装
  ntp server
  wget http://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2.6.tar.gz tar zxvf ntp-4.2.6.tar.gz
  cd ntp-4.2.6
  ./configure --prefix=/usr/local/ntp --enable-all-clocks --enable-parse-clocks make && make install
  注:如以上下载地址无法访问,请从NTP官方下载网页(http://support.ntp.org/bin/view/Main/SoftwareDownloads)寻找下载地址。
  2、修改ntp.conf配置文件
  vi /etc/ntp.conf
  第一种配置:允许任何IP的客户机都可以进行时间同步 将
  restrict default kod nomodify notrap nopeer noquery
  这行修改成:
  restrict default nomodify
  配置文件示例:/etc/ntp.conf
  第二种配置:只允许192.168.18.***网段的客户机进行时间同步在
  restrict default nomodify notrap noquery(表示默认拒绝所有IP的时间同步)
  之后增加一行:
  restrict 192.168.18.0 mask 255.255.255.0 nomodify
  配置文件示例:/etc/ntp.conf
  3、以守护进程启动
  ntpd /usr/local/ntp/bin/ntpd -c /etc/ntp.conf -p /tmp/ntpd.pid
  4、ntpd启动后,客户机要等几分钟再与其进行时间同步,否则会提示“no server suitable for synchronization found”错误。
  三、配置时间同步客户机
  vi /var/spool/cron/root
  增加一行,在每天的5点13分、9点13分、14点13分、19点13分与时间同步服务器进行同步
  13 5,9,14,19 * * * /usr/sbin/ntpdate 192.168.18.2
  备注:如果客户机没有ntpdate,可以下载ntpdate.tar.gz到/usr/sbin/目录,然后解压: wget http://blog.s135.com/attachment/200708/ntdate.tar.gz cd /usr/sbin/ tar zxvf ntpdate.tar.gz

posted @ 2014-04-09 10:53 顺其自然EVO 阅读(238) | 评论 (0)编辑 收藏

数据库Sharding的基本思想和切分策略

 本文着重介绍sharding的基本思想和理论上的切分策略,关于更加细致的实施策略和参考事例请参考我的另一篇博文:数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示
  一、基本思想
  Sharding的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上,从而缓解单一数据库的性能问题。不太严格的讲,对于海量数据的数据库,如果是因为表多而数据多,这时候适合使用垂直切分,即把关系紧密(比如同一模块)的表切分出来放在一个server上。如果表并不多,但每张表的数据非常多,这时候适合水平切分,即把表的数据按某种规则(比如按ID散列)切分到多个数据库(server)上。当然,现实中更多是这两种情况混杂在一起,这时候需要根据实际情况做出选择,也可能会综合使用垂直与水平切分,从而将原有数据库切分成类似矩阵一样可以无限扩充的数据库(server)阵列。下面分别详细地介绍一下垂直切分和水平切分.
  垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非常低,相互影响很小,业务逻辑非常清晰的系统。在这种系统中,可以很容易做到将不同业务模块所使用的表分拆到不同的数据库中。根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也会比较简单清晰。(这也就是所谓的”share nothing”)。
  水平切分于垂直切分相比,相对来说稍微复杂一些。因为要将同一个表中的不同数据拆分到不同的数据库中,对于应用程序来说,拆分规则本身就较根据表名来拆分更为复杂,后期的数据维护也会更为复杂一些。
  让我们从普遍的情况来考虑数据的切分:一方面,一个库的所有表通常不可能由某一张表全部串联起来,这句话暗含的意思是,水平切分几乎都是针对一小搓一小搓(实际上就是垂直切分出来的块)关系紧密的表进行的,而不可能是针对所有表进行的。另一方面,一些负载非常高的系统,即使仅仅只是单个表都无法通过单台数据库主机来承担其负载,这意味着单单是垂直切分也不能完全解决问明。因此多数系统会将垂直切分和水平切分联合使用,先对系统做垂直切分,再针对每一小搓表的情况选择性地做水平切分。从而将整个数据库切分成一个分布式矩阵。
  二、切分策略
  如前面所提到的,切分是按先垂直切分再水平切分的步骤进行的。垂直切分的结果正好为水平切分做好了铺垫。垂直切分的思路就是分析表间的聚合关系,把关系紧密的表放在一起。多数情况下可能是同一个模块,或者是同一“聚集”。这里的“聚集”正是领域驱动设计里所说的聚集。在垂直切分出的表聚集内,找出“根元素”(这里的“根元素”就是领域驱动设计里的“聚合根”),按“根元素”进行水平切分,也就是从“根元素”开始,把所有和它直接与间接关联的数据放入一个shard里。这样出现跨shard关联的可能性就非常的小。应用程序就不必打断既有的表间关联。比如:对于社交网站,几乎所有数据最终都会关联到某个用户上,基于用户进行切分就是最好的选择。再比如论坛系统,用户和论坛两个模块应该在垂直切分时被分在了两个shard里,对于论坛模块来说,Forum显然是聚合根,因此按Forum进行水平切分,把Forum里所有的帖子和回帖都随Forum放在一个shard里是很自然的。
  对于共享数据数据,如果是只读的字典表,每个shard里维护一份应该是一个不错的选择,这样不必打断关联关系。如果是一般数据间的跨节点的关联,就必须打断。
  需要特别说明的是:当同时进行垂直和水平切分时,切分策略会发生一些微妙的变化。比如:在只考虑垂直切分的时候,被划分到一起的表之间可以保持任意的关联关系,因此你可以按“功能模块”划分表格,但是一旦引入水平切分之后,表间关联关系就会受到很大的制约,通常只能允许一个主表(以该表ID进行散列的表)和其多个次表之间保留关联关系,也就是说:当同时进行垂直和水平切分时,在垂直方向上的切分将不再以“功能模块”进行划分,而是需要更加细粒度的垂直切分,而这个粒度与领域驱动设计中的“聚合”概念不谋而合,甚至可以说是完全一致,每个shard的主表正是一个聚合中的聚合根!这样切分下来你会发现数据库分被切分地过于分散了(shard的数量会比较多,但是shard里的表却不多),为了避免管理过多的数据源,充分利用每一个数据库服务器的资源,可以考虑将业务上相近,并且具有相近数据增长速率(主表数据量在同一数量级上)的两个或多个shard放到同一个数据源里,每个shard依然是独立的,它们有各自的主表,并使用各自主表ID进行散列,不同的只是它们的散列取模(即节点数量)必需是一致的。(本文着重介绍sharding的基本思想和理论上的切分策略,关于更加细致的实施策略和参考事例请参考我的另一篇博文:数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示)
  1.事务问题:
  解决事务问题目前有两种可行的方案:分布式事务和通过应用程序与数据库共同控制实现事务下面对两套方案进行一个简单的对比。
  方案一:使用分布式事务
  优点:交由数据库管理,简单有效
  缺点:性能代价高,特别是shard越来越多时
  方案二:由应用程序和数据库共同控制
  原理:将一个跨多个数据库的分布式事务分拆成多个仅处
  于单个数据库上面的小事务,并通过应用程序来总控
  各个小事务。
  优点:性能上有优势
  缺点:需要应用程序在事务控制上做灵活设计。如果使用
  了spring的事务管理,改动起来会面临一定的困难。
  2.跨节点Join的问题
  只要是时行切分,跨节点Join的问明是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。
  3.跨节点的count,order by,group by以及聚合函数问题
  这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。

posted @ 2014-04-09 10:50 顺其自然EVO 阅读(572) | 评论 (0)编辑 收藏

过滤XSS攻击和SQL注入函数

/**
+----------------------------------------------------------
* The goal of this function is to be a generic function that can be used to parse almost any input and     * render it XSS safe. For more information on actual XSS attacks, check out http://ha.ckers.org/xss.html. * Another excellent site is the XSS Database which details each attack and how it works.
* XSS过滤
+----------------------------------------------------------
* @param mixed $value 变量
+----------------------------------------------------------
* @return mixed 过滤后的字符串
+----------------------------------------------------------
*/
function RemoveXSS($val) {
// remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
// this prevents some character re-spacing such as <java\0script>
// note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some          // inputs
$val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
// straight replacements, the user should never need these since they're normal characters
// this prevents like <IMG SRC=@avascript:alert('XSS')>
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++) {
// ;? matches the ;, which is optional
// 0{0,7} matches any padded zeros, which are optional and go up to 8 chars
// @ @ search for the hex values
$val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val);//with a ;
// @ @ 0{0,7} matches '0' zero to seven times
$val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}
// now the only remaining whitespace attacks are \t, \n, and \r
$ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);
$found = true; // keep replacing as long as the previous round replaced something
while ($found == true) {
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i++) {
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(�{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag
$val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
if ($val_before == $val) {
// no replacements were made, so exit the loop
$found = false;
}
}
}
return $val;
}
/*
+----------------------------------------------------------
* 函数名称:inject_check()
+----------------------------------------------------------
* 函数作用:检测提交的值是不是含有SQL注射的字符,防止注射,保护服务器安全
+----------------------------------------------------------
* @param mixed $sql_str: 提交的变量
+----------------------------------------------------------
* @return mixed 返回检测结果,ture or false
+----------------------------------------------------------
*/
function inject_check($sql_str) {
return $sql_str = preg_replace('select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile','', $sql_str);    // 进行过滤
/*
+----------------------------------------------------------
* 函数名称:escapeMysql()
* SQL注入主要是提交不安全的数据给数据库来达到攻击目的。为了防止SQL注入攻击,PHP自带一个功能可以对输入的字符串进行处理,可以在较底层对 * 输入进行安全上的初步处理,也即Magic Quotes。(php.ini magic_quotes_gpc)。如果magic_quotes_gpc选项启用,那么输入的字符串    * 中的单引号,双引号和其它一些字符前将会被自动加上反斜杠\。
* 但Magic Quotes并不是一个很通用的解决方案,没能屏蔽所有有潜在危险的字符,并且在许多服务器上Magic Quotes并没有被启用。所以,我们    * 还需要使用其它多种方法来防止SQL注入。许多数据库本身就提供这种输入数据处理功能。例如PHP的MySQL操作函数中有一个叫                 * mysql_real_escape_string()的函数,可将特殊字符和可能引起数据库操作出错的字符转义。
+----------------------------------------------------------
* 函数作用:检测提交的值是不是含有SQL注射的字符,防止注射,保护服务器安全
+----------------------------------------------------------
* @param mixed $sql_str: 提交的变量
+----------------------------------------------------------
* @return mixed 返回检测结果,ture or false
+----------------------------------------------------------
*/
function escapeMysql($el){
if(is_array($el))
{
return   array_map("escapeMysql",   $el   );
}else{
/*如果Magic Quotes功用启用    */
if (!get_magic_quotes_gpc()) {
$el = mysql_real_escape_string(trim($el));
}
$el    =    str_ireplace("%5d%5c", "'", $el);
$el = str_replace("_", "\_", $el);    // 把 '_'过滤掉
$el = str_replace("%", "\%", $el);    // 把 '%'过滤掉
$el = nl2br($el);    // 回车转换
return   $el;
}
}
/**
+----------------------------------------------------------
* 变量过滤
+----------------------------------------------------------
* @param mixed $value 变量
+----------------------------------------------------------
* @return mixed
+----------------------------------------------------------
*/
function var_filter_deep($value) {
return $value;
if(is_array($value))
{
return $value =   array_map("var_filter_deep",   $value   );
}else{
$value = RemoveXSS($value);
//$value = inject_check($value);
$value = escapeMysql($value);
return $value;
}
}
/*
+----------------------------------------------------------
* 函数名称:verify_id()
+----------------------------------------------------------
* 函数作用:校验提交的ID类值是否合法
+----------------------------------------------------------
* 参  数:$id: 提交的ID值
+----------------------------------------------------------
* 返 回 值:返回处理后的ID
+----------------------------------------------------------
*/
function verify_id($id=null) {
if (!$id) { exit('没有提交参数!'); }    // 是否为空判断
elseif (inject_check($id)) { exit('提交的参数非法!'); }    // 注射判断
elseif (!is_numeric($id)) { exit('提交的参数非法!'); }    // 数字判断
$id = intval($id);    // 整型化
return $id;
}
/**
+----------------------------------------------------------
* 变量安全过滤
+----------------------------------------------------------
* @static
* @access public
+----------------------------------------------------------
* @return string
+----------------------------------------------------------
*/
function varFilter ()
{
$_SERVER = array_map(   "var_filter_deep", $_SERVER );
$_REQUEST = array_map(   "var_filter_deep", $_REQUEST);
$_POST    = array_map(   "var_filter_deep", $_POST   );
$_GET     = array_map(   "var_filter_deep", $_GET    );
$_COOKIE = array_map(   "var_filter_deep", $_COOKIE );
//print_r($_POST);
}

posted @ 2014-04-09 10:43 顺其自然EVO 阅读(808) | 评论 (0)编辑 收藏

LoadRunner 接口测试

Action1()
{
int i;
lr_rendezvous("rend");
lr_start_transaction("get");
for(i=0;i<11;i++)
{
web_url("get",
"URL=http://{urlparam}",
"Resource=1",
"RecContentType=application/json",
"Referer=",
"Snapshot=t1.inf",
LAST);
}
lr_end_transaction("get", LR_AUTO);
lr_start_transaction("post");
web_submit_data("post",
"Action=http://api.zjol.com.cn/api/user_feedback",
"Method=POST",
"RecContentType=text/html",
"Mode=HTML",
ITEMDATA,
"Name=session_id", "Value=4f59d9d14d405ae7367544af", ENDITEM,
"Name=title", "Value=test_title", ENDITEM,
"Name=content", "Value=test data", ENDITEM,
LAST);
web_submit_data("jpost",
"Action=http://api.zjol.com.cn/api/client_log/batch",
"Method=POST",
"RecContentType=application/json",
"Mode=HTML",
ITEMDATA,
"Name=session_id", "Value=4f59d9d14d405ae7367544af", ENDITEM,
"Name=is_test", "Value=false", ENDITEM,
"Name=logs", "Value=[{\"local_time\":1395051006337,\"log_level\":1,\"op_place\":0,\"op_type\":4001},{\"local_time\":1395051094555,\"log_level\":1,\"op_place\":0,\"op_type\":4002},{\"local_time\":1395051115432,\"log_level\":1,\"op_place\":0,\"op_type\":4001},{\"local_time\":1395051132291,\"log_level\":1,\"op_place\":0,\"op_target\":\"52e5f902cf81d754a434fb52\",\"op_type\":2004},{\"local_time\":1395051147817,\"log_level\":1,\"op_place\":0,\"op_type\":5000}]", ENDITEM,
LAST);
lr_end_transaction("post", LR_AUTO);
return 0;
}
  都用了web_submit_data函数,包括存在JSON数据,都能实现。  用web_custom_request函数就没成功过。

posted @ 2014-04-09 10:39 顺其自然EVO 阅读(399) | 评论 (0)编辑 收藏

Appium自动化测试框架构建

Appium简介
  Appium是一个开源、跨平台的测试框架,可以用来测试原生及混合的移动端应用。Appium支持iOS、Android及FirefoxOS平台测试。Appium使用WebDriver的json wire协议,来驱动Apple系统的UIAutomation库、Android系统的UIAutomator框架。
  相比其他的移动自动化测试工具,Appium测试由于调用了Selenium的client库使其可以使用任意的语言,包括PythonRuby、Node.js、Objective-C等。
  Appium下载与安装
  可以去官方下载也可去github下,目前最新的是0.17.6版。当然国内的网盘里也有,这样就不用fan.qiang了。下载后解压即可使用,很方便。
  目前的Appium版本需.net4.5及以上支持,故要想使用Appium还需安装.net4.5.1,可以去微软下载.net4.5.1,当然网盘里也有了,呵呵。
  由于Appium使用了Selenium来进行自动化测试,所以还需安装Selenium,网盘里有下载,Selenium下载地址。下载后解压,切换目录到解压目录,然后执行python setup.py install即可安装Selenium,当然如果您熟悉pip 也可以用pip install Selenium来安装。
  安装、配置Android模拟器
  我们打算在模拟器上使用appium进行自动化测试,故首先得下载安装android开发环境,IDE开发环境配置好以后就可以通过eclipse创建一个android手机模拟器了。
  下载JDK与配置JDK
  下载JDK,下载之后双击压缩包并安装,一路默认下去即可完成,jdk安装到了C:\Java目录下了,其下有两个文件夹jdk1.6.0_13和jre6。
  配置jdk比较简单将C:\Java\jdk1.6.0_13\bin目录添加到系统环境变量path里,记住前加分号,之后确认即可。
  怎样测试java是否配置好?cmd之后键入java -verison
  下载android开发环境ADT-bundle
  可以去Google下载也可以去网盘下载android的集成开发环境。下载后解压到c:\android目录下就行了。
  配置ADT
  将解压目录下的sdk下的tools和platform-tools目录添加到系统环境变量path里,记住前加分号,之后确认即可。
  怎样测试android的ADT是否配置好?进入c:\android\eclipse目录双击eclipse.exe,看能否正常启动、使用。
  新AVD模拟器
  启动eclipse,点击Eclipse软件的Windows菜单,选择其下的Android Virtual Device Manager菜单项,进入Android Virtual Device Manager其对话框后,点击左侧New按钮可新建android手机模拟器。在Create new Android Virtual Device Manager对话框的name文本框内给要创建的模拟器起个名字,例如testAVD01。在Device列表框内选择2.7 QVGA 240 X320 ldpi,在选择了Device后,target列表框自动选择了android 4.4 API Level 19{现在已安装的开发环境里只有一套API,读者可自行安装其他API,届时可任选其他API作为自己测试平台}默认即可,其他选项此时可以不做选择默认设置即可,点击对话框的OK按钮确认,即可创建Android 模拟器。
  启动android模拟器
  cmd之后键入 emulator @testAVD01,等待数秒之后android手机模拟器启动。
  安装、配置Python
  这步比较简单,下载Python后默认安装即可。配置只需把c:\python27目录加到系统环境变量path后即可,记住前加分号。cmd之后键入python如果见到3个大于号即证明python安装配置成功。Appium自动化测试流程
  启动android手机模拟器
  cmd之后键入 emulator @testAVD01,等待数秒之后android手机模拟器启动。
  另起一个cmd键入adb devices查看模拟器是否正常启动了。
  启动Appium
  进入appium解压目录,双击appium.exe文件,点击launch即可。
  基于Appium的Python自动化测试
  编写代码
from selenium import webdriver
# Returns abs path relative to this file and not cwd
PATH = lambda p: os.path.abspath(
os.path.join(os.path.dirname(__file__), p)
)
desired_caps = {}
desired_caps['device'] = 'android'
desired_caps['browserName'] = ''
desired_caps['version'] = '4.2'
desired_caps['app-package'] = 'com.android.calculator2'
desired_caps['app-activity'] = '.Calculator'
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# 点击计算器上的数字和运算符
el = driver.find_element_by_name("1")
el.click()
el = driver.find_element_by_name("5")
el.click()
el = driver.find_element_by_name("9")
el.click()
el = driver.find_element_by_name("delete")
el.click()
el = driver.find_element_by_name("9")
el.click()
el = driver.find_element_by_name("5")
el.click()
el = driver.find_element_by_name("+")
el.click()
el = driver.find_element_by_name("6")
el.click()
el = driver.find_element_by_name("=")
el.click()
driver.quit()
  执行代码
  另起cmd,切到python代码所在目录,执行python xxx.py即可见模拟器在被操作了。

posted @ 2014-04-09 10:32 顺其自然EVO 阅读(7352) | 评论 (0)编辑 收藏

QTP VBScript RegExp对象的运用

 下面来讲讲RegExp对象:
  Vbs提供了针对正则表达式的一个非常实用的类,就是RegExp
Global属性:代表全局匹配
IgnoreCase属性:大小写忽略
Pattern属性:正则表达式
Execute方法:匹配搜索,返回匹配结果集合
Replace方法:匹配代替,返回替代匹配结果
Test方法:测试匹配,返回布尔类型
  下面举几个实例:
'判断正则匹配是否正确
'msgbox (IsRegMatch("a123","http://www.123.456.com"))
Function IsRegMatch(patrn,str)
Dim regEx
Set regEx = New RegExp
regEx.Pattern = patrn
regEx.IgnoreCase = False
IsRegMatch = regEx.Test(str)
Set regEx = nothing
End Function
'替换匹配字符串
'msgbox (ReplaceRegMatch("9","loader runner 9.0, qtp 9.0","10"))
Function ReplaceRegMatch(patrn,str,replaceStr)
Dim regEx
Set regEx = New RegExp
regEx.Pattern = patrn
regEx.IgnoreCase = False
regEx.Global = True   'false的时候只会替换第一个匹配的字符串。若为true则会替换所有匹配的字符串
ReplaceRegMatch = regEx.Replace(str,replaceStr)
End Function
'返回匹配内容
'returnRegMatch "qtp .","qtp 1 qtp 2 qtp3 qtp 4"
Function ReturnRegMatch(patrn,str)
Dim regEx,matches,match
Set regEx = New RegExp
regEx.Pattern = patrn
regEx.IgnoreCase = true
regEx.Global = true  '打开全局搜索
Set matches = regEx.Execute(str)
For Each match in matches
print cstr(match.firstIndex) + " " + match.value + " " + cstr(match.length)
Next
End Function

posted @ 2014-04-09 10:31 顺其自然EVO 阅读(454) | 评论 (0)编辑 收藏

项目中遇到的问题

 产品总算上线了,历时两个半月的时间,出了内测,公测,beta版本,1.0.1版本,中间有段时间累得够呛,但上线之后,感觉努力还是值的。
  在整个测试过程中,遇到了一些问题,及在遇到这些问题后所采取的措施
  1. 提测质量差
  问题描述:第一个提测版本差,有些均未通过冒烟测试
  问题分析
  A. 版本提测质量差,但基于发布时间已在,因此,在提测差时就开始测试
  提测质量差的点:- 基于上每项功能的完成度都不高 - 有些功能均未实现 -
  B. 新的团队,团队处于磨合期
  C. 在提测时,对提测要求不明确,在时间点到后,匆忙提测
  解决方式:
  明确版本提测要求,并且开发得到了足够的时间。
  2. 版本控制
  问题描述:
  最初阶段,每天出一个版本,基于新版本测试,记录每个版本上测试的功能。版本过于频繁,质量把控不好
  问题分析:
  A. 基于版本提测质量差,而且每天出一个版本,差上加差,
  B. 虽然记录每个版本上测试的功能,但仍然无法把控当前版本的质量状况。
  解决方式:暂停每天发布一个版本
  前期:将全功能版本作为下一个版本发布目标,但由于一些功能并没有完成,因而,全功能版本分成了好几个阶段
  后期:以测试一轮周期,发布最新版本
  3. 功能反复
  问题描述:在上一个版本是OK的功能,在新版本中功能失常
  功能反复分两点:一是大功能反复, 二是小功能(如:某个bug)反复
  问题分析:
  大功能反复:情况主要发生成项目前期和中期
  A. 功能未完成,在完善功能时,未考虑到与该功能相关的点
  B. 在提测之后,发现一些问题,导致了整个模块重构,重构后导致了问题的反复
  小功能反复:这个情况主要发生在项目中后期
  A. 因为项目里的部分开发是外援的,在项目中期时,撤出了团队,新接手的人员,对代码不熟悉,在修改bug时,经常出来顾此失彼
  B. 开发小一在修改代码时,动到了小二的代码,导致了小二出了问题
  解决方式:
  对大功能反复,是这么处理:冒烟测试由开发来完成,冒烟通过后,再交由测试
  对小功能反复 ,没有有效的处理方式,测试这边可以做的是,加强测试,这个问题,在发布前夕好了很多,但问题仍然存在  4. 需求不明确,前后不一致
  问题描述:需求不明确,特别在一些边界,各端统一上
  问题分析:
  A. 交互文档经历6任交互,最后一任交互只参与两个模块的定义,现任交互对于以往交互了解不够深入
  B. 产品提测时,交互验证不足
  解决方式:
  由于项目已提测,因此在整个周期里,对于交互需求方面的疑问直接找相关人员去确认。
  在后期的小版本中,我们把这类问题尽量控制在提测之前(详见小版本里的改进与问题)
  5. 测试和开发信息不对称
  问题描述:测试获取到的消息,与产品实现的方式不一致,如:有的功能定义了,但产品并未实现或实现方式与定义不一致
  问题分析:
  A. 在开发阶段,测试并未参与讨论需求,还在其他项目里
  B. 需求重新确认后,没有及时通知测试
  解决方式:
  强调消息需要通知到测试,现在阶段,如果因这种类型而引起的问题,将建ticket,指派给相关人员
  小版本里的改进与问题
  现存在问题:
  1. 现对Release版本会做RC checklist, 进行最后版本的质量控制,
  但会存在一些问题,在小版本提测时,就已经存在,而冒烟测试是测不到的,在最后做checklist时,才发现
  改进点:
  1. 需求疑问在提测之前尽量提出,并且通知到开发,在开发阶段便把该问题解决
  测试在开发阶段跟踪产品进度
  在写测试用例时,就把问题抛出。
  2. 提测流程:
  对功能方面的ticket,交互在提测之前便在开发机器上验证,通过后再提测
  把不符合交互预期的问题,在提测之前更改,节约了时间,避免问题在提测后才提出

posted @ 2014-04-09 10:28 顺其自然EVO 阅读(241) | 评论 (0)编辑 收藏

Linux crontab 实现每秒执行

 linux crontab 命令,最小的执行时间是一分钟。如需要在小于一分钟内重复执行,可以有两个方法实现。
  1.使用延时来实现每N秒执行
  创建一个php做执行动作,非常简单,就是把当前时间写入log。
  <?php
  file_put_contents('/home/fdipzone/php/crontab/run.log', date('Y-m-d H:i:s')."\r\n", FILE_APPEND);
  ?>
  crontab -e 输入以下语句,然后 :wq 保存退出。
* * * * * php /home/fdipzone/php/crontab/tolog.php
* * * * * sleep 10; php /home/fdipzone/php/crontab/tolog.php
* * * * * sleep 20; php /home/fdipzone/php/crontab/tolog.php
* * * * * sleep 30; php /home/fdipzone/php/crontab/tolog.php
* * * * * sleep 40; php /home/fdipzone/php/crontab/tolog.php
* * * * * sleep 50; php /home/fdipzone/php/crontab/tolog.php
  使用 tail -f 查看执行情况,可以见到log每10秒被写入一条记录。
fdipzone@ubuntu:~$ tail -f /home/fdipzone/php/crontab/run.log
2014-03-31 21:47:01
2014-03-31 21:47:11
2014-03-31 21:47:21
2014-03-31 21:47:31
2014-03-31 21:47:41
2014-03-31 21:47:51
2014-03-31 21:48:01
  原理:通过延时方法 sleep N  来实现每N秒执行。
  注意:
  60必须能整除间隔的秒数(没有余数),例如间隔的秒数是2,4,6,10,12等。
  如果间隔的秒数太少,例如2秒执行一次,这样就需要在crontab 加入60/2=30条语句。不建议使用此方法,可以使用下面介绍的第二种方法。
  2.编写shell脚本实现
crontab.sh
#!/bin/bash
step=2 #间隔的秒数,不能大于60
for (( i = 0; i < 60; i=(i+step) )); do
$(php '/home/fdipzone/php/crontab/tolog.php')
sleep $step
done
exit 0
  crontab -e 输入以下语句,然后:wq 保存退出。
  # m h  dom mon dow   command
  * * * * * /home/fdipzone/php/crontab/crontab.sh

  使用 tail -f 查看执行情况,可以见到log每2秒被写入一条记录。
fdipzone@ubuntu:~/php/crontab$ tail -f run.log
2014-03-31 22:23:01
2014-03-31 22:23:03
2014-03-31 22:23:06
2014-03-31 22:23:08
2014-03-31 22:23:10
2014-03-31 22:23:12
2014-03-31 22:23:14
2014-03-31 22:23:16
2014-03-31 22:23:18
2014-03-31 22:23:20
2014-03-31 22:23:22
2014-03-31 22:23:25
2014-03-31 22:23:27
2014-03-31 22:23:29
2014-03-31 22:23:31
2014-03-31 22:23:33
2014-03-31 22:23:35
2014-03-31 22:23:37
2014-03-31 22:23:39
2014-03-31 22:23:41
2014-03-31 22:23:44
2014-03-31 22:23:46
2014-03-31 22:23:48
2014-03-31 22:23:50
2014-03-31 22:23:52
2014-03-31 22:23:54
2014-03-31 22:23:56
2014-03-31 22:23:58
2014-03-31 22:24:00
  原理:在sh使用for语句实现循环指定秒数执行。
  注意:如果60不能整除间隔的秒数,则需要调整执行的时间。例如需要每7秒执行一次,就需要找到7与60的最小公倍数,7与60的最小公倍数是420(即7分钟)。
  则 crontab.sh step的值为7,循环结束条件 i<420, crontab -e可以输入以下语句来实现
  # m h  dom mon dow   command
  */7 * * * * /home/fdipzone/php/crontab/crontab.sh

posted @ 2014-04-08 10:58 顺其自然EVO 阅读(3636) | 评论 (1)编辑 收藏

MySQL数据库设置远程访问

  1、修改localhost
  更改 "mysql" 数据库里的 "user" 表里的 "host" 项,从"localhost"改成"%"
  mysql>use mysql;
  mysql>update user set host = '%' where user = 'root';
  mysql>select host, user from user;
  mysql>FLUSH PRIVILEGES;
  2、指定授权
  使用myuser/mypassword从任何主机连接到mysql服务器:
  GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'%' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
  使用myuser/mypassword从ip为192.168.225.166的主机连接到mysql服务器:
  GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'192.168.225.166' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
  3、泛授权
  mysql -h localhost -u root
  mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; //赋予任何主机上以root身份访问数据的权限
  mysql>FLUSH PRIVILEGES;

posted @ 2014-04-08 10:57 顺其自然EVO 阅读(160) | 评论 (0)编辑 收藏

使用PDO查询mysql避免SQL注入

 使用传统的 mysql_connect 、mysql_query方法来连接查询数据库时,如果过滤不严紧,就有SQL注入风险。虽然可以用mysql_real_escape_string()函数过滤用户提交的值,但是也有缺陷。而使用PHP的PDO扩展的 prepare 方法,就可以避免sql injection 风险。
  PDO(PHP Data Object) 是PHP5新加入的一个重大功能,因为在PHP 5以前的php4/php3都是一堆的数据库扩展来跟各个数据库的连接和处理,如 php_mysql.dll。 PHP6中也将默认使用PDO的方式连接,mysql扩展将被作为辅助 。官方地址:http://php.net/manual/en/book.pdo.php
  1. PDO配置
  使用PDO扩展之前,先要启用这个扩展,php.ini中,去掉"extension=php_pdo.dll"前面的";"号,若要连接数据库,还需要去掉与PDO相关的数据库扩展前面的";"号(一般用的是php_pdo_mysql.dll),然后重启Apache服务器即可。
  extension=php_pdo.dll
  extension=php_pdo_mysql.dll
  2. PDO连接mysql数据库
  $dbh = new PDO("mysql:host=localhost;dbname=mydb","root","password");
  默认不是长连接,若要使用数据库长连接,可以在最后加如下参数:
  $dbh = new PDO("mysql:host=localhost;dbname=mydb","root","password","array(PDO::ATTR_PERSISTENT => true) ");
  $dbh = null; //(释放)
  3. PDO设置属性
  PDO有三种错误处理方式:
  PDO::ERrmODE_SILENT不显示错误信息,只设置错误码
  PDO::ERrmODE_WARNING显示警告错
  PDO::ERrmODE_EXCEPTION抛出异常
  可通过以下语句来设置错误处理方式为抛出异常
  $db->setAttribute(PDO::ATTR_ERrmODE, PDO::ERrmODE_EXCEPTION);
  因为不同数据库对返回的字段名称大小写处理不同,所以PDO提供了PDO::ATTR_CASE设置项(包括PDO::CASE_LOWER,PDO::CASE_NATURAL,PDO::CASE_UPPER),来确定返回的字段名称的大小写。
  通过设置PDO::ATTR_ORACLE_NULLS类型(包括PDO::NULL_NATURAL,PDO::NULL_EmpTY_STRING,PDO::NULL_TO_STRING)来指定数据库返回的NULL值在php中对应的数值。
  4. PDO常用方法及其应用
  PDO::query() 主要是用于有记录结果返回的操作,特别是SELECT操作
  PDO::exec() 主要是针对没有结果集合返回的操作,如INSERT、UPDATE等操作
  PDO::prepare() 主要是预处理操作,需要通过$rs->execute()来执行预处理里面的SQL语句,这个方法可以绑定参数,功能比较强大(防止sql注入就靠这个)
  PDO::lastInsertId() 返回上次插入操作,主键列类型是自增的最后的自增ID
  PDOStatement::fetch() 是用来获取一条记录
  PDOStatement::fetchAll() 是获取所有记录集到一个集合
  PDOStatement::fetchColumn() 是获取结果指定第一条记录的某个字段,缺省是第一个字段
  PDOStatement::rowCount() :主要是用于PDO::query()和PDO::prepare()进行DELETE、INSERT、UPDATE操作影响的结果集,对PDO::exec()方法和SELECT操作无效。  5.PDO操作MYSQL数据库实例
<?php
$pdo = new PDO("mysql:host=localhost;dbname=mydb","root","");
if($pdo -> exec("insert into mytable(name,content) values('fdipzone','123456')")){
echo "insert success";
echo $pdo -> lastinsertid();
}
?>
<?php
$pdo = new PDO("mysql:host=localhost;dbname=mydb","root","");
$rs = $pdo -> query("select * from table");
$rs->setFetchMode(PDO::FETCH_ASSOC); //关联数组形式
//$rs->setFetchMode(PDO::FETCH_NUM); //数字索引数组形式
while($row = $rs -> fetch()){
print_r($row);
}
?>
<?php
foreach( $db->query( "SELECT * FROM table" ) as $row )
{
print_r( $row );
}
?>
  统计有多少行数据:
<?php
$sql="select count(*) from table";
$num = $dbh->query($sql)->fetchColumn();
?>
  prepare方式:
<?php
$query = $dbh->prepare("select * from table");
if ($query->execute()) {
while ($row = $query->fetch()) {
print_r($row);
}
}
?>
  prepare参数化查询:
<?php
$query = $dbh->prepare("select * from table where id = ?");
if ($query->execute(array(1000))) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
print_r($row);
}
}
?>
  使用PDO访问MySQL数据库时,真正的real prepared statements 默认情况下是不使用的。为了解决这个问题,你必须禁用 prepared statements的仿真效果。下面是使用PDO创建链接的例子:
  <?php
  $dbh = new PDO('mysql:dbname=mydb;host=127.0.0.1;charset=utf8', 'root', 'pass');
  $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
  ?>
  setAttribute()这一行是强制性的,它会告诉 PDO 禁用模拟预处理语句,并使用 real parepared statements 。这可以确保SQL语句和相应的值在传递到mysql服务器之前是不会被PHP解析的(禁止了所有可能的恶意SQL注入攻击)。
  虽然你可以配置文件中设置字符集的属性(charset=utf8),但是需要格外注意的是,老版本的 PHP( < 5.3.6)在DSN中是忽略字符参数的。
  完整的代码使用实例:
<?php
$dbh = new PDO("mysql:host=localhost; dbname=mydb", "root", "pass");
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //禁用prepared statements的仿真效果
$dbh->exec("set names 'utf8'");
$sql="select * from table where username = ? and password = ?";
$query = $dbh->prepare($sql);
$exeres = $query->execute(array($username, $pass));
if ($exeres) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
print_r($row);
}
}
$dbh = null;
?>
  上面这段代码就可以防范sql注入。为什么呢?
  当调用 prepare() 时,查询语句已经发送给了数据库服务器,此时只有占位符 ? 发送过去,没有用户提交的数据;当调用到 execute()时,用户提交过来的值才会传送给数据库,它们是分开传送的,两者独立的,SQL攻击者没有一点机会。
  但是我们需要注意的是以下几种情况,PDO并不能帮助你防范SQL注入。
  不能让占位符 ? 代替一组值,这样只会获取到这组数据的第一个值,如:
  select * from table where userid in ( ? );
  如果要用in來查找,可以改用find_in_set()实现
  $ids = '1,2,3,4,5,6';
  select * from table where find_in_set(userid, ?);
  不能让占位符代替数据表名或列名,如:
  select * from table order by ?;
  不能让占位符 ? 代替任何其他SQL语法,如:
  select extract( ? from addtime) as mytime from table;

posted @ 2014-04-08 10:50 顺其自然EVO 阅读(195) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 128 129 130 131 132 133 134 135 136 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜