发布新日志

  • Eclipse中使用ANT

    2011-04-27 11:10:48

    前言

      ant是java开发者工具箱的重要一环,junit,xdoclet等都与它紧密关联,程序员可能习惯了IDE提供的自动构建,甚至部署的功能,从而忽略了ant本身,其实,主流的IDE通常是内置ant任务来完成这些工作的,熟悉ant内在的机理,可以阅读或简单修改build.xml无疑可以帮助你更灵活地集成、管理应用项目,如果需要学习maven这种开源项目管理解决方案,也是要以理解ant为基础的哟。另外,使用ant的过程实际上对构建进行了文档化,它是无关于IDE的,想象一下,你的同事中可能三分之一在用JbuilderX,三分之一用eclipse,还有一些是别的。

      本人使用eclipse3.0.1,以前的构建和发布工作都由myeclipse插件作了,趁周末实践了一下手动构建,记此备忘。

      实践

      准备工作:这是我的个人习惯,把所有公用的类库jar置于一个固定目录,分好类,不要丢在一个文件夹下,如jakarta-commons、hibernate、spring、struts等,这些是源码构建时需要用到的,在部署时可能有一些不用再打进去了,比如servlet.jar。如果你们有自己的framework,也一并放在这里。然后,打开eclipse,进入Windows->Preferences->Java->User Libraries,增加一个自己的库,比如说mylib,把刚才那些公共的jar全部添入,这样有个好处,在eclipse项目中,不用再看到烦人的长长的jar列表了,比较整洁。

      下来正式进行:

      1.新建一个Java Project,此时就不要再选你的j2ee插件内置的一些选项了,至简即可。

      2.在root下建几个文件夹,我们在网上下载的开源项目中经常可以看到这些,比如:

      src - 源码
      classes - 编译
      web - jsp等
      lib - 库,这里可以简单地把mylib下的东东copy过来,便于将来发布源码。
      dlist - 输出的jar或war

      当然,我们要建一个build.xml,eclipse中会出现一个蚂蚁的小图标,一般这个文件建立后,下一个项目简单的copy过去,稍加改动就可以了。

      3.打开项目的属性页,在Java Build Path的库选项中,加入我们自定义的公共库mylib.至于Builders方式就不用改了,使用默认的Java Builer即可,我只是项目部署时使用ant,平常的排错工作就交给IDE吧。

      4.重中之重,写你的build.xml,网上文章很海,我这里就不再啰嗦了,基本上就分那几个任务:

      4.1 先要声明一些路径变量,如

      <property name="war.dir" value="dlist" />

      也可以将其写至properties文件中,在这里引用;

      4.2 声明编译的类路径,如下:

      <path id="master-classpath">
      <fileset dir="${lib.root}/struts">
      <include name="struts-menu-2.3.jar" />
      <include name="struts.jar" />
      </fileset>
      <fileset dir="${lib.root}/jakarta-commons">
      <include name="commons-*.jar" />
      </fileset>
      <fileset dir="${lib.root}/ibatis2.0.9">
      <include name="ibatis-*.jar" />
      </fileset>
      <fileset dir="${lib.root}/jdbcdriver">
      <include name="jtds-0.9-rc2.jar" />
      </fileset>s
      ......
      </path>

      4.3 清空输出目录,如web,dlist等。

      4.4 编译构建:

      <target name="build" description="Compile main source tree java files into class files, generate jar files">

      <mkdir dir="${build.dir}" />

      <javac destdir="${build.dir}" source="1.3" target="1.3" debug="true" deprecation="false" ptimize="false" failonerror="true">
      <src path="${src.dir}" />
      <classpath refid="master-classpath" />
      </javac>

      <copy todir="${build.dir}" preservelastmodified="true">
      <fileset dir="${src.dir}">
      <include name="**/*.xml" />
      <include name="**/*.properties" />
      </fileset>
      </copy>
      <!-- ============================================= -->
      <!-- 据测试,资源文件不能被打到jar文件中,其余均可 -->
      <!-- ============================================= -->
      <copy todir="${webclasses.dir}/conf" preservelastmodified="true">
      <fileset dir="${src.dir}/conf">
      <include name="springResources*.properties" />
      </fileset>
      </copy>

      <mkdir dir="${weblib.dir}" />

      <jar jarfile="${weblib.dir}/${name}.jar" compress="true">
      <fileset dir="${build.dir}">
      <include name="**" />
      </fileset>
      </jar>

      <copy todir="${weblib.dir}" preservelastmodified="true">

      <fileset dir="${lib.root}">
      <include name="log4j-1.2.8.jar" />
      </fileset>
      <fileset dir="${lib.root}/struts">
      <include name="struts-menu-2.3.jar" />
      <include name="struts.jar" />
      </fileset>
      <fileset dir="${lib.root}/jakarta-commons">
      <include name="commons-*.jar" />
      </fileset>
      <fileset dir="${lib.root}/spring-1.1.3">
      <include name="spring.jar" />
      <include name="aopalliance.jar" />
      </fileset>
      ......

      </copy>

      </target>

      <!-- ============================================= -->
      <!-- Compile main Java sources and copy libraries -->
      <!-- ============================================= -->
      <target name="warfile" description="Build the web application archive">

      <mkdir dir="${dist.dir}" />
      <war warfile="${dist.dir}/${name}.war" basedir="${war.dir}" webxml="${war.dir}/WEB-INF/web.xml">
      <include name="*" />
      <include name="WEB-INF/*.*" />
      <exclude name="WEB-INF/web.xml" />
      <include name="WEB-INF/classes/*.*" />
      <include name="WEB-INF/lib/**" />
      <exclude name="**/.*" />
      </war>

      </target>


      4.5 打成war

      <target name="warfile" description="Build the web application archive">

      <mkdir dir="${dist.dir}" />
      <war warfile="${dist.dir}/${name}.war" basedir="${war.dir}" webxml="${war.dir}/WEB-INF/web.xml">
      <include name="*" />
      <include name="WEB-INF/*.*" />
      <exclude name="WEB-INF/web.xml" />
      <include name="WEB-INF/classes/*.*" />
      <include name="WEB-INF/lib/**" />
      <exclude name="**/.*" />
      </war>

      </target>

      4.6 把几个任务串起来,弄一个default target

      <target name="all">
      <antcall target="clean" />
      <antcall target="build" />
      <antcall target="warfile" />
      </target>

      打完收功。在实践中发现,一些配置文件,如struts-config.xml ibatis和spring的xml都可以打进jar文件,spring资源文件好象不行,得单独copy至WEB-INFclasses下,另外,你的web文件夹下,事先得放好web.xml,以及一些tld文件哟。
  • .深入了解超线程、双核CPU、双CPU与单CPU的区别

    2011-04-27 10:43:02

    做好产品需求文档的这十步,是经过长期的实践经验和反复验证而得到的。可能这里描述的不是很全面,但他已经足够让你做一个成功的产品需求文档。做好这几步花费的时间要以项目的大小、复杂程度、个体学识、基本技能熟练度而定。

      第一步:做好准备工作

      你要做的是一个让人无可争议的产品,为了做好他,你必须做好前期的准备工作。你需要去了解你的顾客、竞争对手、产品团队的实力和需要的技术。你需要从顾客、用户、竞争对手、分析师、产品团队、销售队伍、市场、公司职员等收集他们能发现的问题和可能的解决办法。这里有很多的工作需要你去完成,在“成功的产品背后”这篇文章中有详细的描述。

      建立良好的交流也非常重要,它会影响着产品团队。如果你的准备工作做的够好,你也会变得越来越有信心和说服力。

      第二步:确定产品的目的

      任何一个好的产品都开始于一个需求。你必须清楚的了解这个需求,你的产品如何达到这个需求。

      产品经理需要提出一个清晰、简明的价值主张,让它很容易被接受,要让产品团队、管理人员、用户、市场人员清楚的明白这个产品到底是什么意图。虽然这听起来很简单,但是也只有少数产品才有这样的价值主张。考虑“velevator pitch ”(电梯间演讲、电梯行销)测试。假设你在做电梯的时候遇到公司CEO,他问你产品的意图是什么,你能在电梯到达之前回答这个问题吗?如果不能,你就还有工作需要做。也许是你的说明没有针对性,他可能表现出来和其他产品做的没有什么明显区别;也许你提出的观点不能和你的用户产生共鸣;也许你解决的是一个非常规的问题,可能你想应用一种技术。这个价值主张可能需要满足公司的产品战略。注意你不需要阐述太多的细节,从某些方面来说,一个有价值的观点应当是越简越好。

       产品需求需要确切的指出这个产品发布的目标,同样的这个目标也有优先之分。例如,你的目标可能是:1)易用,2)零售价不足$100,3)和前期产品很好的结合。然后你需要说明如何去测算。对于“易用”这类项目,你需要明确指出产品可用性达到某个水平。这是通常用目标用户来定义。可用性工程师能测算出你的产品对目标用户的可用性,也测算出可用性问题的严重程度,同样你可以说明没有重大的可用性问题。

      这里的关键就是让每个人都知道产品成功的时候是什么样,还有给产品团队在设计和实施中遇到问题如何进行取舍的指导。

      第三步:确定用户原型、用户目标和用户任务

      现在你已经明白你想要解决什么问题,下接下来就要深入了解目标用户和顾客,在这步中,和你的PD(产品设计)紧密联系非常重要。

      用户原型

      在这个阶段,PM需要和很多用户交流,需要花费大量的时间去直接观察和讨论。现在我们需要对用户和顾客进行分类,然后决定那一类是我们的首要用户。

      比如你正在做一个像eBay一样的互联网拍卖服务,你同时拥有买家和卖家,在这之中还有使用频率少的用户和经常使用的用户,不难想象还有个别特殊的用户,比如团体公司采购者。

      PM(产品经理)和PD(产品设计)需要首先确定类型是最重要的,然后尽量对这个用户群的特征进行详细的描述,以便使用这个模型去指导产品的设计。这个模型通常称其为“人物角色”。 虽然是想像的,但是应该是典型的、可行的和真实的,让你能够使用。这个想法来自与一个能代表这类用户的本质的原型。

      举个例子:

      “里昂是一个超级卖家,46岁,男性,居住在Fresno,经营小型摩托车配件。虽然他开着一个小店,但是他的生意大部分来自Ebay,每个月平均有400多次交易。他出售的东西品种非常多,但是他最受欢迎的商品还是哈雷戴维森的负重袋。他自己拥有两个哈雷,还开着1993年的丰田皮卡。里昂已经结婚了还有两个小孩。

      里昂买电脑仅仅是因为他需要使用Ebay,除了ebay和电子邮件很少再使用其他东西。里昂已经在Ebay上销售产品已经三年了,他学会了在ebay应该掌握的东西,他非常自豪的拥有超过5000的信用度。如果Ebay更改了网站,特别是销售的过程方面,对于他来说改变习惯、学习这些变更是非常困难的。 里昂已经形成了自己的习惯,星期一列出销售的商品,星期五拍卖结束,设法让在收到货款的几个小时内出货。”

      但愿这样的描述能让你了解里昂和知道他是怎么来的。当我们考虑新功能时,我就要问问自己里昂会是什么发应,为了让他能顺利的使用这个功能我们需要做什么。

      注意缩小范围,让他仅仅描绘必不可少的。满足所有人是徒劳的,通常最后没人会满意,所以尽量提出几个最重要的和最流行的角色描述是非常重要的。同样,如果你不去精确的定位你的目标用户,你就只会存在模糊的概念,你会发现理解你用户的反应非常困难。你要倾向于设想,让你能更像你的用户。


      用户目标(用户意愿)

      一旦我们确定并描绘了我们主要的用户类型,我们就需要找出用户在使用产品中的目标(想要干什么).这听起来很简单,但是解开根本问题是非常具有挑战性的,特别当你周围的人告诉你你已经解决了他们想要的。

      从CEO、销售代表、工程师到客户,每个人都太兴奋而不能帮助你找到解决根本问题的办法,他们会告诉你在某个地方添加一个快捷按钮,或则添加一个功能仅仅是因为竞争对手有,或则是改变成他们喜欢的颜色。

      最好的解决办法取决于清晰的了解到底什么问题需要解决,每个用户模型可能有不同的目的,需要在用户原型涉及的方面中进行寻找。有可能将来某个功能解决的问题并不是主要用户需要达到的目标之一。


      用户任务(tasks,用户为达到目标使用产品而需要做的任务)

      掌握了用户原型与他们的目标愿望,我们就开始着手设计任务来满足他们的目标意愿,这是产品制作进程中最核心的部分,也是创造力和创新力被激发的地方。

      许多优秀的产品仅是用更好更新的办法解决一个已有的问题,有时候这种办法仅仅是应用一个种新技术,但是大部分是来自深刻的见解而使一种新方法的产生。例如TiVo(美国市场占有率第一的数字录像机)在电视节目录制的老问题上面想出一个全新的办法,让顾客更加容易地实现他们的目标并且建立了电子设备一个全新的类别。

      注意我们虽然谈到了目标和任务但是还没有谈到具体的功能,这些功能都需要达到用户目标而必须的。你以后会发现许多功能都是低优先级或则是完全多余的。

      以“必须功能”这个理由可以排除很多功能。讽刺的是,你用越少的功能,你的产品被发现得越来越强大。这是因为产品的功能越少,你的用户就会发现并使用更多的功能,成功的使用越来越多的功能他们就认为你的产品非常强大。这些理由都是违反我们直觉的,我们大多数人都不能和我们的用户一样,我们在自己的行业中愿意比用户花费更多的时间去探索功能和容忍复杂性。

      第四步:定义产品原则

      现在你需要开始把你的需求和用户体验定义成详细的要求。同时你仍然会面临着许多的决定和权衡,为你的产品标准作出最佳的决定是非常重要的。

      在大多数的产品团队中,每个成员都有做好产品的原则,但很少有两个人有同样的想法,这些差异都会导致不可思议的结果。
    尝试和制订一系列指导整个团队的产品原则是非常有价值的,这些原则需要具体到域名和项目。

      用TiVo举例,在产品团队工作开始时,以下这些产品规范就被建立,并在团队里传达:

        1.它是娱乐的
        2.一个傻瓜式的电视
        3.一个该死的视频设备
        4.平滑柔顺的
        5.没有模式和深层次
        6.尊重观众的隐私权
        7.像电视一样强大

      这些规范很大的影响到产品的定义而且在很大程度上加大了难度,但是他们确实是成功产品的来源。比如易趣的口号就是:1、易于使用 2、安全 3、有趣

      它将在该项目中,在面对众多问题而作出决定的时候进行指南.

      第五步:产品原型和检验

      这是一个拿出你想法的阶段,创造力和创新力拿出成就的地方.

      很多人都容易犯一个常见的错误,他们对产品设计规范太有信心,结果一旦得到beta的测试他们就必须调整产品。但是肯定beta测试版并不是进行重大改变的时候,所以才会有许多首次发布的产品离目标太远。

      对于许多产品来说,这个时候你可以用大量的原型做很多的实验。首先,下面的三个非常重要的测试你可能需要做

      可行性测试
      一个直接的问题就是产品是否可以开发,你的工程师和设计师应当介入技术的可行性调查和探索可用办法。有些办法是行不通的,但是有其他的办法可行是非常有希望的。
      工程师会发现在产品的某个阶段不可能逾越,现在知道比以后知道要好。

      可用性测试
      产品设计师将要和你紧密工作共同提出产品功能,让它能适应不同的用户。可用性测试常常会找出遗漏的产品要求,同时确认产品最初的要求是否是必须的。在你拿出一个成功的用户体验之前需要多做一些测试工作。可用性的目的是在真正的用户身上测试,从产品目标用户得到质量反馈的测试是非常艺术和科学的。当然产品经理和产品设计将模仿使用,但是实际是没有人能取代真实的目标用户。

      概念测试(Product Concept Testing)
      光是可用和可行是不足的。真正的问题是你的用户想要购买吗—你的用户有多喜欢-你做的有什么价值。这测试可能与可用性测试联系在一起。

      对于一部份小产品,您的想法写在纸就足够了,但是对于多数产品,为了预计产品是否达到目标,复杂用户互作用或新技术的使用、某种形式原型都是非常重要的。

      原型也许是一个物理设备,或者它也许是软件产品的一个预览版本。关键是它需要足够现实,您能用原型在实际目标顾客身上测试,并且他们可以给您质量反馈。

      以前做原型主要有两个障碍。第一是缺乏良好的原型工具,需要花费很多的时间制作原型;另一个是管理方不知道原型和真实产品的区别,在不可预计的情况下,按照最终产品来要求原型。

      今天有优秀的原型设计工具可以让工程师或设计师快速的制作原型,可以有效的模拟未来的产品以达到必要的程度让实际用户进行测试。而且大多数管理者都知道模仿和实际的区别 — 就如同缩小比例的房子模型和真实的家一样。

      在实际去做产品之前去检验你的产品是非常重要的。一旦实际的工程开始,作出重要的变动会变得非常困难,花费也会变得很高。

      第六步:验证和质疑

      当你认为你弄懂了你需要解决的问题,现在是时候开始验证和质疑假设。

      假设甚至当作不知道是很容易的,但是切勿把不可知的结论当作指引,那会妨碍你获得成功。天文学最初定义是研究太阳和其他行星如何围绕自己转,本身的定义就是一个臆断,反而阻止人们获得真相。

      第七步:写

      当然你需要把这些都写下来,大多数的PRD都是word文档,但也有一些是帮助文档,PowerPoint,或则写在白纸上。当然用什么格式不是很重要,重要的是让团队成功能轻松的看懂,不会遗漏,还有就是PRD可以随着项目开发而更新。

      记住对话是两个人之间的,但是PRD是要沟通整个小组。你也要记住获得产品的销售才是是重要的,所以不必担心要有什么漂亮的外观、PRD写的有多厚,只要它是可读的、可理解的、是需要的内容。

      PRD文档主要有四个部份组成

      产品用途
      你的工作就是指出目标,团队需要知道他们的目的是什么,目标说明要尽可能的明确,请确保你的内容包括:
      *那些问题你要解决,不是解决方案
      *谁是目标用户
      *细节很多,但是大图片必须清晰
      *情景描述
      多开展集思广益的会议和临时口头的讨论,从而更好的写出来,更会让团队深入了解。

      产品功能特性
      产品需求文档最主要的当然是需求。 具体的需求完全地将取决于您的领域,但是不管你是什么行业,您的产品团队将受益于陈述需求的清楚,毫不含糊的要求,而不是模糊的解决方案。
      描述每个功能的互动设计和使用案例。您必须非常清楚每个功能和用户体验,还需要给工程团队留下足够多的灵活自主空间。
      同样重要的是确定那些要求满足哪个目的。这里就需要提到“需求跟踪”,对于关键的产品这是一个重要的流程。每种产品规范可能受益于清楚确定那些要求满足哪个目的,如果某人决定削减要求,想要深入了解就会非常困难。 从要求到目的明确说明将会是文档更加清晰。

      发布标准
      发布标准经常是不断变化的,但是好的PRD应该考虑到为每种标准定一个最低要求。典型的如:性能,可测量性,可靠性,可用性,可控性。

      时间进度
      其中很困难的一个问题就是描述产品需要的时间进度表。随便列出一个时间是没用的,你需要描述环境、动机、预计目标。你需要整个团队都和你一样达到预计目标,最终完成一个成功的产品。

      第八步 优先级

      除了明确的要求,对每一个您的要求给予优先和排列秩序是很重要的。多数产品经理,如果他们给予优先级,一般都是表明要求是否是“必须有, “重要”或“希望拥有” (或其他一些分类系统)。分类是很重要的,不可掉以轻心。

      产品经理对任何一个标记“必须拥有”都需要有高度的标准。如果还没有找到必须拥有的功能意味着产品还不应该产生。所以小心标注“必须拥有”,这些标注“必须拥有”的功能直接反应出产品的核心价值。
      “重要”的分类也很重要,在产品销售前只要有机会就要满足这些功能。
      “希望拥有”产品团队也应该注意到,即使大多数也都没有实现,在未来版本也适当的慢慢实现。

      这些有时候是不够的,从1到n每一个分类优先排序都是很重要的。有几个原因:
      首先,上市时间总是被关注,并且日程表经常下降,您说不定被迫使削减有些特点为了尽快进入市场。 你也不想产品团队先开发简单的功能而放松重要的功能,导致最后客户使用的关键功能还没完成。
      其次,在产品设计和开发阶段,团队将会发现更多的问题产生并解决这些问题,所以很有可能有更多关键功能出现。优先顺序会可以帮助你如何平衡以容纳更多的功能。
      这点就是说产品经理如何不给出优先级和重要等级,其他相关较少的因素也会跟着无法确定。

      整个PRD是一个不断完善和思维提高的过程,明朗锐利就是可以成功的产品的,模糊就是失败的产品。在争论最激烈的时候也能容易做决定,并且帮助工程师做出计划。

      第九步 测试完整性

      现在你有一个PRD草稿,你需要测试它的完整性。工程师是否可以充分了解并达到目标?OA Team(质量管理团队)是否有足够的信息来做出测试计划,是否可以开始做案例?

      当投资人或相关人审核了PRD,确定了各个需要说明的方面,所有的问题得到解决,现在你就可以按PRD进行产品开发。

      第十步 管理产品

      在产品实施期间,就算是最好PRD,也有不计其数的问题被解决。解决所有PRD中存在问题,如果不在PRD中就写进去。你的任务就是迅速解决问题并记录在PRD。

      如果你做了你的工作并准备记录在PRD,项目审查就会变得非常简单,因为任何一个部份都历历在目。

      记住PRD是一个“活”的文件,在要跟踪记录在产品开发期间的所有功能过程。最后你会发现很多额外的东西,如果你认为是必要的就在PRD中写进。

  • 产品需求这十步

    2011-04-27 10:41:24

    做好产品需求文档的这十步,是经过长期的实践经验和反复验证而得到的。可能这里描述的不是很全面,但他已经足够让你做一个成功的产品需求文档。做好这几步花费的时间要以项目的大小、复杂程度、个体学识、基本技能熟练度而定。

      第一步:做好准备工作

      你要做的是一个让人无可争议的产品,为了做好他,你必须做好前期的准备工作。你需要去了解你的顾客、竞争对手、产品团队的实力和需要的技术。你需要从顾客、用户、竞争对手、分析师、产品团队、销售队伍、市场、公司职员等收集他们能发现的问题和可能的解决办法。这里有很多的工作需要你去完成,在“成功的产品背后”这篇文章中有详细的描述。

      建立良好的交流也非常重要,它会影响着产品团队。如果你的准备工作做的够好,你也会变得越来越有信心和说服力。

      第二步:确定产品的目的

      任何一个好的产品都开始于一个需求。你必须清楚的了解这个需求,你的产品如何达到这个需求。

      产品经理需要提出一个清晰、简明的价值主张,让它很容易被接受,要让产品团队、管理人员、用户、市场人员清楚的明白这个产品到底是什么意图。虽然这听起来很简单,但是也只有少数产品才有这样的价值主张。考虑“velevator pitch ”(电梯间演讲、电梯行销)测试。假设你在做电梯的时候遇到公司CEO,他问你产品的意图是什么,你能在电梯到达之前回答这个问题吗?如果不能,你就还有工作需要做。也许是你的说明没有针对性,他可能表现出来和其他产品做的没有什么明显区别;也许你提出的观点不能和你的用户产生共鸣;也许你解决的是一个非常规的问题,可能你想应用一种技术。这个价值主张可能需要满足公司的产品战略。注意你不需要阐述太多的细节,从某些方面来说,一个有价值的观点应当是越简越好。

       产品需求需要确切的指出这个产品发布的目标,同样的这个目标也有优先之分。例如,你的目标可能是:1)易用,2)零售价不足$100,3)和前期产品很好的结合。然后你需要说明如何去测算。对于“易用”这类项目,你需要明确指出产品可用性达到某个水平。这是通常用目标用户来定义。可用性工程师能测算出你的产品对目标用户的可用性,也测算出可用性问题的严重程度,同样你可以说明没有重大的可用性问题。

      这里的关键就是让每个人都知道产品成功的时候是什么样,还有给产品团队在设计和实施中遇到问题如何进行取舍的指导。

      第三步:确定用户原型、用户目标和用户任务

      现在你已经明白你想要解决什么问题,下接下来就要深入了解目标用户和顾客,在这步中,和你的PD(产品设计)紧密联系非常重要。

      用户原型

      在这个阶段,PM需要和很多用户交流,需要花费大量的时间去直接观察和讨论。现在我们需要对用户和顾客进行分类,然后决定那一类是我们的首要用户。

      比如你正在做一个像eBay一样的互联网拍卖服务,你同时拥有买家和卖家,在这之中还有使用频率少的用户和经常使用的用户,不难想象还有个别特殊的用户,比如团体公司采购者。

      PM(产品经理)和PD(产品设计)需要首先确定类型是最重要的,然后尽量对这个用户群的特征进行详细的描述,以便使用这个模型去指导产品的设计。这个模型通常称其为“人物角色”。 虽然是想像的,但是应该是典型的、可行的和真实的,让你能够使用。这个想法来自与一个能代表这类用户的本质的原型。

      举个例子:

      “里昂是一个超级卖家,46岁,男性,居住在Fresno,经营小型摩托车配件。虽然他开着一个小店,但是他的生意大部分来自Ebay,每个月平均有400多次交易。他出售的东西品种非常多,但是他最受欢迎的商品还是哈雷戴维森的负重袋。他自己拥有两个哈雷,还开着1993年的丰田皮卡。里昂已经结婚了还有两个小孩。

      里昂买电脑仅仅是因为他需要使用Ebay,除了ebay和电子邮件很少再使用其他东西。里昂已经在Ebay上销售产品已经三年了,他学会了在ebay应该掌握的东西,他非常自豪的拥有超过5000的信用度。如果Ebay更改了网站,特别是销售的过程方面,对于他来说改变习惯、学习这些变更是非常困难的。 里昂已经形成了自己的习惯,星期一列出销售的商品,星期五拍卖结束,设法让在收到货款的几个小时内出货。”

      但愿这样的描述能让你了解里昂和知道他是怎么来的。当我们考虑新功能时,我就要问问自己里昂会是什么发应,为了让他能顺利的使用这个功能我们需要做什么。

      注意缩小范围,让他仅仅描绘必不可少的。满足所有人是徒劳的,通常最后没人会满意,所以尽量提出几个最重要的和最流行的角色描述是非常重要的。同样,如果你不去精确的定位你的目标用户,你就只会存在模糊的概念,你会发现理解你用户的反应非常困难。你要倾向于设想,让你能更像你的用户。


      用户目标(用户意愿)

      一旦我们确定并描绘了我们主要的用户类型,我们就需要找出用户在使用产品中的目标(想要干什么).这听起来很简单,但是解开根本问题是非常具有挑战性的,特别当你周围的人告诉你你已经解决了他们想要的。

      从CEO、销售代表、工程师到客户,每个人都太兴奋而不能帮助你找到解决根本问题的办法,他们会告诉你在某个地方添加一个快捷按钮,或则添加一个功能仅仅是因为竞争对手有,或则是改变成他们喜欢的颜色。

      最好的解决办法取决于清晰的了解到底什么问题需要解决,每个用户模型可能有不同的目的,需要在用户原型涉及的方面中进行寻找。有可能将来某个功能解决的问题并不是主要用户需要达到的目标之一。


      用户任务(tasks,用户为达到目标使用产品而需要做的任务)

      掌握了用户原型与他们的目标愿望,我们就开始着手设计任务来满足他们的目标意愿,这是产品制作进程中最核心的部分,也是创造力和创新力被激发的地方。

      许多优秀的产品仅是用更好更新的办法解决一个已有的问题,有时候这种办法仅仅是应用一个种新技术,但是大部分是来自深刻的见解而使一种新方法的产生。例如TiVo(美国市场占有率第一的数字录像机)在电视节目录制的老问题上面想出一个全新的办法,让顾客更加容易地实现他们的目标并且建立了电子设备一个全新的类别。

      注意我们虽然谈到了目标和任务但是还没有谈到具体的功能,这些功能都需要达到用户目标而必须的。你以后会发现许多功能都是低优先级或则是完全多余的。

      以“必须功能”这个理由可以排除很多功能。讽刺的是,你用越少的功能,你的产品被发现得越来越强大。这是因为产品的功能越少,你的用户就会发现并使用更多的功能,成功的使用越来越多的功能他们就认为你的产品非常强大。这些理由都是违反我们直觉的,我们大多数人都不能和我们的用户一样,我们在自己的行业中愿意比用户花费更多的时间去探索功能和容忍复杂性。

      第四步:定义产品原则

      现在你需要开始把你的需求和用户体验定义成详细的要求。同时你仍然会面临着许多的决定和权衡,为你的产品标准作出最佳的决定是非常重要的。

      在大多数的产品团队中,每个成员都有做好产品的原则,但很少有两个人有同样的想法,这些差异都会导致不可思议的结果。
    尝试和制订一系列指导整个团队的产品原则是非常有价值的,这些原则需要具体到域名和项目。

      用TiVo举例,在产品团队工作开始时,以下这些产品规范就被建立,并在团队里传达:

        1.它是娱乐的
        2.一个傻瓜式的电视
        3.一个该死的视频设备
        4.平滑柔顺的
        5.没有模式和深层次
        6.尊重观众的隐私权
        7.像电视一样强大

      这些规范很大的影响到产品的定义而且在很大程度上加大了难度,但是他们确实是成功产品的来源。比如易趣的口号就是:1、易于使用 2、安全 3、有趣

      它将在该项目中,在面对众多问题而作出决定的时候进行指南.

      第五步:产品原型和检验

      这是一个拿出你想法的阶段,创造力和创新力拿出成就的地方.

      很多人都容易犯一个常见的错误,他们对产品设计规范太有信心,结果一旦得到beta的测试他们就必须调整产品。但是肯定beta测试版并不是进行重大改变的时候,所以才会有许多首次发布的产品离目标太远。

      对于许多产品来说,这个时候你可以用大量的原型做很多的实验。首先,下面的三个非常重要的测试你可能需要做

      可行性测试
      一个直接的问题就是产品是否可以开发,你的工程师和设计师应当介入技术的可行性调查和探索可用办法。有些办法是行不通的,但是有其他的办法可行是非常有希望的。
      工程师会发现在产品的某个阶段不可能逾越,现在知道比以后知道要好。

      可用性测试
      产品设计师将要和你紧密工作共同提出产品功能,让它能适应不同的用户。可用性测试常常会找出遗漏的产品要求,同时确认产品最初的要求是否是必须的。在你拿出一个成功的用户体验之前需要多做一些测试工作。可用性的目的是在真正的用户身上测试,从产品目标用户得到质量反馈的测试是非常艺术和科学的。当然产品经理和产品设计将模仿使用,但是实际是没有人能取代真实的目标用户。

      概念测试(Product Concept Testing)
      光是可用和可行是不足的。真正的问题是你的用户想要购买吗—你的用户有多喜欢-你做的有什么价值。这测试可能与可用性测试联系在一起。

      对于一部份小产品,您的想法写在纸就足够了,但是对于多数产品,为了预计产品是否达到目标,复杂用户互作用或新技术的使用、某种形式原型都是非常重要的。

      原型也许是一个物理设备,或者它也许是软件产品的一个预览版本。关键是它需要足够现实,您能用原型在实际目标顾客身上测试,并且他们可以给您质量反馈。

      以前做原型主要有两个障碍。第一是缺乏良好的原型工具,需要花费很多的时间制作原型;另一个是管理方不知道原型和真实产品的区别,在不可预计的情况下,按照最终产品来要求原型。

      今天有优秀的原型设计工具可以让工程师或设计师快速的制作原型,可以有效的模拟未来的产品以达到必要的程度让实际用户进行测试。而且大多数管理者都知道模仿和实际的区别 — 就如同缩小比例的房子模型和真实的家一样。

      在实际去做产品之前去检验你的产品是非常重要的。一旦实际的工程开始,作出重要的变动会变得非常困难,花费也会变得很高。

      第六步:验证和质疑

      当你认为你弄懂了你需要解决的问题,现在是时候开始验证和质疑假设。

      假设甚至当作不知道是很容易的,但是切勿把不可知的结论当作指引,那会妨碍你获得成功。天文学最初定义是研究太阳和其他行星如何围绕自己转,本身的定义就是一个臆断,反而阻止人们获得真相。

      第七步:写

      当然你需要把这些都写下来,大多数的PRD都是word文档,但也有一些是帮助文档,PowerPoint,或则写在白纸上。当然用什么格式不是很重要,重要的是让团队成功能轻松的看懂,不会遗漏,还有就是PRD可以随着项目开发而更新。

      记住对话是两个人之间的,但是PRD是要沟通整个小组。你也要记住获得产品的销售才是是重要的,所以不必担心要有什么漂亮的外观、PRD写的有多厚,只要它是可读的、可理解的、是需要的内容。

      PRD文档主要有四个部份组成

      产品用途
      你的工作就是指出目标,团队需要知道他们的目的是什么,目标说明要尽可能的明确,请确保你的内容包括:
      *那些问题你要解决,不是解决方案
      *谁是目标用户
      *细节很多,但是大图片必须清晰
      *情景描述
      多开展集思广益的会议和临时口头的讨论,从而更好的写出来,更会让团队深入了解。

      产品功能特性
      产品需求文档最主要的当然是需求。 具体的需求完全地将取决于您的领域,但是不管你是什么行业,您的产品团队将受益于陈述需求的清楚,毫不含糊的要求,而不是模糊的解决方案。
      描述每个功能的互动设计和使用案例。您必须非常清楚每个功能和用户体验,还需要给工程团队留下足够多的灵活自主空间。
      同样重要的是确定那些要求满足哪个目的。这里就需要提到“需求跟踪”,对于关键的产品这是一个重要的流程。每种产品规范可能受益于清楚确定那些要求满足哪个目的,如果某人决定削减要求,想要深入了解就会非常困难。 从要求到目的明确说明将会是文档更加清晰。

      发布标准
      发布标准经常是不断变化的,但是好的PRD应该考虑到为每种标准定一个最低要求。典型的如:性能,可测量性,可靠性,可用性,可控性。

      时间进度
      其中很困难的一个问题就是描述产品需要的时间进度表。随便列出一个时间是没用的,你需要描述环境、动机、预计目标。你需要整个团队都和你一样达到预计目标,最终完成一个成功的产品。

      第八步 优先级

      除了明确的要求,对每一个您的要求给予优先和排列秩序是很重要的。多数产品经理,如果他们给予优先级,一般都是表明要求是否是“必须有, “重要”或“希望拥有” (或其他一些分类系统)。分类是很重要的,不可掉以轻心。

      产品经理对任何一个标记“必须拥有”都需要有高度的标准。如果还没有找到必须拥有的功能意味着产品还不应该产生。所以小心标注“必须拥有”,这些标注“必须拥有”的功能直接反应出产品的核心价值。
      “重要”的分类也很重要,在产品销售前只要有机会就要满足这些功能。
      “希望拥有”产品团队也应该注意到,即使大多数也都没有实现,在未来版本也适当的慢慢实现。

      这些有时候是不够的,从1到n每一个分类优先排序都是很重要的。有几个原因:
      首先,上市时间总是被关注,并且日程表经常下降,您说不定被迫使削减有些特点为了尽快进入市场。 你也不想产品团队先开发简单的功能而放松重要的功能,导致最后客户使用的关键功能还没完成。
      其次,在产品设计和开发阶段,团队将会发现更多的问题产生并解决这些问题,所以很有可能有更多关键功能出现。优先顺序会可以帮助你如何平衡以容纳更多的功能。
      这点就是说产品经理如何不给出优先级和重要等级,其他相关较少的因素也会跟着无法确定。

      整个PRD是一个不断完善和思维提高的过程,明朗锐利就是可以成功的产品的,模糊就是失败的产品。在争论最激烈的时候也能容易做决定,并且帮助工程师做出计划。

      第九步 测试完整性

      现在你有一个PRD草稿,你需要测试它的完整性。工程师是否可以充分了解并达到目标?OA Team(质量管理团队)是否有足够的信息来做出测试计划,是否可以开始做案例?

      当投资人或相关人审核了PRD,确定了各个需要说明的方面,所有的问题得到解决,现在你就可以按PRD进行产品开发。

      第十步 管理产品

      在产品实施期间,就算是最好PRD,也有不计其数的问题被解决。解决所有PRD中存在问题,如果不在PRD中就写进去。你的任务就是迅速解决问题并记录在PRD。

      如果你做了你的工作并准备记录在PRD,项目审查就会变得非常简单,因为任何一个部份都历历在目。

      记住PRD是一个“活”的文件,在要跟踪记录在产品开发期间的所有功能过程。最后你会发现很多额外的东西,如果你认为是必要的就在PRD中写进。

  • 增强网站易用性的10个设计技巧

    2011-04-27 10:37:10

    易用性是什么?

    易用性就是是你的网站对用户来说使用更简单,能够让用户在他需要的地方很快找到需要的信息。类比于Google所提倡的”让用户呆在Google的时间不短缩短“,对于网站来说,我们不是减少用户在网站的停留时间,而是缩短用户寻找关键信息和向导的时间。

    很多人认为要实现网站的易用性需要耗费大量的人力、财力和物力,确实有很多的大网站他们投入了很多的钱和设备去做网站的易用性的研究和测试,但是对于我们日常的小网站来说,我们仍有方法在没有专家和专业设备的基础上改进网站的易用性。

    一、包含宣传词(Tagline)。

    宣传词是一个用来表明公司理念、目标或者网站愿景的地方。这一部分应该是网站最引人瞩目的部分,应该用简短的语言概括站点。统计结果表明,一个页面只有8秒钟的时间来吸引一个用户继续他的浏览,所以如果不能用醒目的焦点吸引用户,那这个页面就是失败的。

    国外的站点很多使用Tagline或者醒目的Flash,国内的站点也可以 这么用,有的时候内容多的时候,还可以是Banner+焦点图,这个要区分不同的网站类型来对待。例如对于产品型的网站,完全可以只使用一个焦点图或者醒目的有特色的Flash来吸引注意,如果是资讯类的或者专题类的页面,则可能既要有Banner,来突出这个页面的主题,也要有焦点图,来显示最近的一些动态。这个需要在制作的过程中不断地体会积累。

     

     

     

    二、提供站内搜索。

    站内搜索对于用户来说也是非常重要的,特别是当站点的内容量开始逐渐增多以至于用户不能很轻易的找到他想要的东西时,用户往往会想到搜索。 你能想象到,要在博客园里通过连接一个一个查找你之前看到过的某一篇文章的痛苦吧。

    搜索框的长度和位置也需要加一点注意,不能太小,位置最好放在右上区域,因为根据用户的浏览习惯呈现出”F“的趋势(F Pattern),提交按钮的文字最好能够明确的告诉用户,接下来将要发生的动作是搜索。

     

    三、不能滥用图片。

     从易用性的角度来看,Less Is Always More。

    四、使用站点地图。

    站点地图是一项能够改进站内导航和搜索引擎优化(SEO)的特性。典型的站点地图提供了站点的结构和各个页面的导航。站点地图可以是任何形式的,可以是一个网页、一些页面的列表,只要他们是按照层级关系组织起来的就行。

    最近,Google、Yahoo、MSN开始提供Sitemap Protocol的服务,同站点地图非常相近,但是数据是以XML的形式组织的。

     

    五、不要破坏工作流。

    工作流是指用户在网站上所进行的操作,比如填写表单、注册用户、浏览目录、档案等,要允许用户撤销操作,如果没有提供后退或者返回的选项,用户就被逼着做他们不想做的动作,或者他们会干脆关掉浏览器来图个清净。

    有些站点上的操作顺序并不是那么的明显,这个时候就需要有提示来指导用户。比如Yahoo Music当第一次进入的时候,会有一个向导来指导你一步一步的熟悉页面上各个功能区。玩游戏的时候也会有游戏教学这个环节,如果网站上也提供了,会带来很好的用户体验。

    还有一个误区是改变超链接的样式。传统的门户往往让超链接停留在他原始的样子,这样能够给用户明确的指示,这是一个超链接,通过点击,我可以进入一个新的页面。当然,我并不赞成超链接的样式一定不能改变,但是如果发生了改变,我们一定需要通过图示或者文字来表明,用户可以通过点击这个链接到达另外一个页面。

    六、让网站更容易被”扫描“。

    内容的可读性能够提高用户的忠诚度,让用户停留在站点上获得他们需要的内容。但是研究表明,很多用户并不是在读页面,而是在”扫描“,通过扫描标题、着重文字、强调的列表来获得信息。

    Jacbo Nilsen通过视觉追踪发现用户的浏览很像一个F型,他们从左到用从上到下的”扫描“页面。

     

    他的实验同时也得出了一下结论:

    用户不是一个字一个字的去阅读,而是从段落、着重文字中提取信息。

     文章的前两段非常的重要,这两段必须包括这篇文章的大部分内容。

    副标题和列表能够重新引起用户的注意,注意用这些元素来强调重要的内容。

    从传统的纸质媒体上得来的经验也告诉我们,将内容组织成一个倒金字塔型。关键问题是,我们如何才能知道什么信息对用户来说是重要的,那些信息对用户来说又是不重要的,作者推荐了一个工具:News Values。

    七、不要设计容易误导的界面元素。

    千万不要设计那种看上去像是一个按钮实际上不是的内容,我们也经常被那些带下划线的文字误导,当我们点击时,发现他们根本不是链接!

    Yahoo是一个很好的正面的例子。

    八、给出用户有意义的提示。

    这一点大家应该都有共识了,不要将出错信息直接输出到页面上,要给出用户经过处理的、用户能够理解的信息。

    九、不要过度使用Javascript。

    过度的使用Javascript和Ajax技术,我们需要小心的避免出现浏览器兼容性问题,我们要很好的衡量这个代价。

    十、避免验证符。

    验证符的使用在必须的地方添加,如果不是那么必要,还是要符合人类懒惰的本性,去掉那些验证符吧。

     

    总结:提升网站的易用性并不一定需要墨守成规,但是在没有足够的功力之前,这些规则能够为我们提供一个很好的方向指向。对于这些规则的争论也很多,比如避免验证符的使用,在很多情况下,我们必须使用验证符来避免垃圾信息的产生,就像坐飞机时的安检,虽然是一件让人不舒服的事情,可是他有他的目的。

     网站的易用性也不是网站的全部,我们必须在易用性、页面设计、站点的可维护性和安全性之间权衡,对不同类型的项目采取不同的处理策略。

    参考资料:

    [1]、http://www.webdesignerdepot.com/2008/12/10-usability-tips-for-web-designers/

    [2]、http://www.useit.com/

    [3]、News Values。http://mtsu32.mtsu.edu:11178/171/newsvals.htm#top

  • 免费的Linux集群解决方案:LVS

    2011-04-27 10:28:05

    LVS,Linux Virtual Server,网站:www.linuxvirtualserver.org/

    LVS是针对Linux服务器的集群解决方案。

    LVS对最终用户完全透明,就好像是一台高性能虚拟的服务器。

    可作为负载均衡解决方案应用于需要高扩展能力,高可用性的网络服务。

    如Web服务,缓存,邮件,FTP,媒体和VoIP服务等。

    该软件遵守GPL协议。

    基本功能和应用


    见图:

      免费的Linux集群解决方案:LVS - kingofwolves - kingofwolves的博客

    基本机制:

    • 基于网络第四层传输层的软件解决方案;
    • 负载均衡服务器支持HA(可通过心跳线切换主均衡服务器到备份服务器);
    • Real Server可以根据业务情况随时增加。
    是否可以在Real Server上部署Tomcat集群?

    是的,Tomcat的集群技术是第七层应用层的集群技术,LVS是第四层的集群技术,二者没有冲突。

    使用LVS替代Apache+Tomcat的集群方案

    Apache的负载均衡方案是应用层的技术,性能和LVS有很大差距。

    LVS可以替代Apache,结合Tomcat自身的集群功能,实现负载均衡功能。
  • WEB 服务器的集群

    2011-04-27 10:27:09

    集群(Cluster)
        所谓集群是指一组独立的计算机系统构成的一个松耦合的多处理器系统,它们之间通过网络实现进程间的通信。应用程序可以通过网络共享内存进行消息传送,实现分布式计算机。
    负载均衡(Load Balance)  
        网络的负载均衡是一种动态均衡技术,通过一些工具实时地分析数据包,掌握网络中的数据流量状况,把任务合理均衡地分配出去。这种技术基于现有网络结构,提供了一种扩展服务器带宽和增加服务器吞吐量的廉价有效的方法,加强了网络数据处理能力,提高了网络的灵活性和可用性。
    特点
     (1)高可靠性(HA)。利用集群管理软件,当主服务器故障时,备份服务器能够自动接管主服务器的工作,并及时切换过去,以实现对用户的不间断服务。
    (2)高性能计算(HP)。即充分利用集群中的每一台计算机的资源,实现复杂运算的并行处理,通常用于科学计算领域,比如基因分析、化学分析等。
    (3)负载平衡。即把负载压力根据某种算法合理分配到集群中的每一台计算机上,以减轻主服务器的压力,降低对主服务器的硬件和软件要求。
    LVS系统结构与特点
    1. Linux Virtual Server:简称LVS。是由中国一个Linux程序员章文嵩博士发起和领导的,基于Linux系统的服务器集群解决方案,其实现目标是创建一个具有良好的扩展性、高可靠性、高性能和高可用性的体系。许多商业的集群产品,比如RedHat的Piranha、 Turbo Linux公司的Turbo Cluster等,都是基于LVS的核心代码的。
    2. 体系结构:使用LVS架设的服务器集群系统从体系结构上看是透明的,最终用户只感觉到一个虚拟服务器。物理服务器之间可以通过高速的 LAN或分布在各地的WAN相连。最前端是负载均衡器,它负责将各种服务请求分发给后面的物理服务器,让整个集群表现得像一个服务于同一IP地址的虚拟服务器。
    3. LVS的三种模式工作原理和优缺点: Linux Virtual Server主要是在负载均衡器上实现的,负载均衡器是一台加了 LVS Patch的2.2.x版内核的Linux系统。LVS Patch可以通过重新编译内核的方法加入内核,也可以当作一个动态的模块插入现在的内核中。
    负载均衡器可以运行在以下三种模式下:
    (1)Virtual Server via NAT(VS-NAT):用地址翻译实现虚拟服务器。地址转换器有能被外界访问到的合法IP地址,它修改来自专有网络的流出包的地址。外界看起来包是来自地址转换器本身,当外界包送到转换器时,它能判断出应该将包送到内部网的哪个节点。优点是节省IP 地址,能对内部进行伪装;缺点是效率低,因为返回给请求方的流量经过转换器。
    (2)Virtual Server via IP Tunneling (VS-TUN):用IP隧道技术实现虚拟服务器。这种方式是在集群的节点不在同一个网段时可用的转发机制,是将IP包封装在其他网络流量中的方法。为了安全的考虑,应该使用隧道技术中的VPN,也可使用租用专线。 集群所能提供的服务是基于TCP/IP的Web服务、Mail服务、News服务、DNS服务、Proxy服务器等等.
    (3)Virtual Server via Direct Routing(VS-DR):用直接路由技术实现虚拟服务器。当参与集群的计算机和作为控制管理的计算机在同一个网段时可以用此法,控制管理的计算机接收到请求包时直接送到参与集群的节点。优点是返回给客户的流量不经过控制主机,速度快开销少。
      以四台服务器为例实现负载均衡:
      安装配置LVS
      1. 安装前准备:
      (1)首先说明,LVS并不要求集群中的服务器规格划一,相反,可以根据服务器的不同配置和负载状况,调整负载分配策略,充分利用集群环境中的每一台服务器。如下表:
      Srv Eth0 Eth0:0 Eth1 Eth1:0

      vs1 10.0.0.1 10.0.0.2 192.168.10.1 192.168.10.254

      vsbak 10.0.0.3 192.168.10.102

      real1 192.168.10.100

      real2 192.168.10.101

      其中,10.0.0.2是允许用户访问的IP。
      (2)这4台服务器中,vs1作为虚拟服务器(即负载平衡服务器),负责将用户的访问请求转发到集群内部的real1,real2,然后由real1,real2分别处理。 Client为客户端测试机器,可以为任意操作系统。
      (3)所有OS为redhat6.2,其中vs1 和vsbak 的核心是2.2.19, 而且patch过ipvs的包, 所有real server的Subnet mask 都是24位, vs1和vsbak 的10.0.0. 网段是24 位。
      2.理解LVS中的相关术语
      (1) ipvsadm :ipvsadm是LVS的一个用户界面。在负载均衡器上编译、安装ipvsadm。
      (2) 调度算法: LVS的负载均衡器有以下几种调度规则:Round-robin,简称rr;weighted Round-robin,简称wrr;每个新的连接被轮流指派到每个物理服务器。Least-connected,简称lc;weighted Least-connected,简称wlc,每个新的连接被分配到负担最小的服务器。
      (3) Persistent client connection,简称pcc,(持续的客户端连接,内核2.2.10版以后才支持)。所有来自同一个IP的客户端将一直连接到同一个物理服务器。超时时间被设置为360秒。Pcc是为https和cookie服务设置的。在这处调度规则下,第一次连接后,所有以后来自相同客户端的连接(包括来自其它端口)将会发送到相同的物理服务器。但这也会带来一个问题,因为大约有25%的Internet 可能具有相同的IP地址。
      (4) Persistent port connection调度算法:在内核2.2.12版以后,pcc功能已从一个调度算法(你可以选择不同的调度算法:rr、wrr、lc、wlc、pcc)演变成为了一个开关选项(你可以让rr、 wrr、lc、wlc具备pcc的属性)。在设置时,如果你没有选择调度算法时,ipvsadm将默认为wlc算法。 在Persistent port connection(ppc)算法下,连接的指派是基于端口的,例如,来自相同终端的80端口与443端口的请求,将被分配到不同的物理服务器上。不幸的是,如果你需要在的网站上采用cookies时将出问题,因为http是使用80端口,然而cookies需要使用443端口,这种方法下,很可能会出现cookies不正常的情况。
     (5)Load Node Feature of Linux Director:让Load balancer 也可以处理users 请求。
    (6)IPVS connection synchronization。
     (7)ARP Problem of LVS/TUN and LVS/DR:这个问题只在LVS/DR,LVS/TUN 时存在。
  • 高负载网站架构

    2011-04-27 10:26:05

    1, LVS做前端四层软件均衡负载
    LVS是基于IP虚拟分发的规则, 不同于apache,squid这些7层基于http协议的反向代理软件, 前者在性能上能得到更好的保证!
    另外, 后者在处理http header信息时, 会显得很被动.

    开源, 高性能, 这不就是我们所需要的吗?

    另外, 针对大访问量, 还可以使用DNS轮询+LVS集群.
    当然, 比起硬件均衡负载, 单点故障的风险会更大.

    2,squid 做前端静态页面缓存, 包括 css, javascript
    squid 是业内公认的优秀代理服务器,其缓存能力更让许多高负载网站青睐!(比如新浪,网易等)
    使用他, 通过本机内存+ 磁盘的集群存储方案, 能够起到很好的加速作用!

    使用squid, 也是大部分网站的节约成本之道.

    3, lighttpd 提供图片, css, javascript. 服务. 做到静态与动态分离.
    采用lighttpd, 而不使用apache, 是因为它对静态内容的响应速度高于apache一到三倍.
    这对于高负载网站是梦寐以求的.

    加上, 在其前端部署了squid, 真正做到了, 超高命中率, 超快响应速度.

    3,apache 用来处理php, url重定向, url过滤, 防洪水攻击等等.
    apache是业内主流http服务器,比较看重它的稳定性, 扩展性.
    使用它, 制作一些推广页面, 一些需要快速开发的页面, 最好不过了.

    最重要的是, 它可以使用mod_jk或mod_proxy对复杂业务请求的进行代理.
    比如, 将用户注册, 代理给jboss, 用java开发.

    需要提一下的是, apache的module开发.
    一句话 - 非常实用.
    你可以只用apache提供的类库, 就能很方便的开发一个http的日志处理模块.

    另外, 它也可以与squid 集成, 从而, 形成一条很完美的加速链.

    4,JBOSS 用来处理含复杂的业务逻辑与充当JAVAEE容器的角色
    JBOSS是red hat旗下的优秀中间件产品,在java开源领域小有名气,并且完全支持j2ee规范的,功能非常强大
    使用他,既能保证业务流程的规范性,又可以节省开支(免费的)

    java的优势, 就不多说了.


    5,mysql数据库
    使用mysql数据库,单机达到百万级别的数据存储,及快速响应,应该是没问题的.
    如果网站本身访问增长很快, 可以考虑mysql 集群.

    从而获得高伸缩性, 高访问性能.

    不管是通过 master+slaver的主从结构.还是根据业务进行分表.
    mysql的集群特性, 都是网站首选的.

    6,memcache作为分布式缓存
    基于中央存放的缓存载体, 一般都需要集群.
    基于c写的memcache, 可以很自豪的顶起高性能缓存的帽子.
    它几乎可以缓存任何数据. 包括 html, java对象, 文件等等.

    重要的是, 它给jboss, apache等服务器实现高效的缓存方案, 提供了有力的保证.



    LVS

    ======================================
    .....
    apache mod_jk / mod_proxy+ jboss
    apache mod_jk / mod_proxy+ jboss
    .....
    squid + lighttpd
    squid + lighttpd
    ....
    =================================
    ....
    mysql + memcache
    mysql + memcache
    ......
    ================================
  • LoadRunner参数化

    2011-04-27 10:24:32

    一、关于参数的定义
    函数中参数的值就是在录制过程中输入的实际值。
    例如,你录制了一个 Web 应用程序的脚本。脚本生成器生成了一个声明,该声明搜索名称为 “软件测试” 的图书的数据库。当你用多个虚

    拟用户和迭代回放脚本时,也许你不想重复使用相同的值“ 软件测试”,还需要其他的值如“项目管理” 。例如平常经常用到的,登陆界

    面输入用户名和密码,那么,你就可以用参数来取代这个常量。结果就是你可以用指定的数据源的数值来取代参数值。数据源可以是一个文

    件,也可以是内部产生的变量。
    用参数表示用户的脚本有两个优点:① 可以使脚本的长度变短。② 可以使用不同的数值来测试你的脚本。例如,如果你企图搜索不同名称

    的图书,你仅仅需要写提交函数一次。在回放的过程中,你可以使用不同的参数值,而不只搜索一个特定名称的值。
    参数化包含以下两项任务:① 在脚本中用参数取代常量值。② 设置参数的属性以及数据源。
    参数化仅可以用于一个函数中的参量。你不能用参数表示非函数参数的字符串。另外,不是所有的函数都可以参数化的
    二、参数的创建
    可以指定名称和类型来创建参数。不存在对脚本中参数个数的限制。在 Web 程序的用户脚本中,你可以使用如下过程在基于文本的脚本视图

    中创建参数。或者,也可以在基于图标的树形视图中创建参数。
    在基于文本的脚本视图中创建一个参数:
    1 、 将光标定位在要参数化的字符上,点击右键。打开弹出菜单。
    2 、 在弹出菜单中,选择 “Replace with a Parameter” 。选择或者创建参数的对话框弹出。
    3 、 在 “Parameter name” 中输入参数的名称,或者选择一个在参数列表中已经存在的参数。
    4 、 在 “Parameter type” 下拉列表中选择参数类型
    1)、 Date/Time    Date/Time 用当前的日期 / 时间替换参数。要指定一个 Date/Time 格式,你可以从菜单列表中选择格式,或者指定

    你自己的格式。这个格式应该和你脚本中录制的 Date/Time 格式保持一致。你可以设置一个时间参数的偏移量,如果你打算测试下个月的日

    期,你就可以选择偏移量为30。你也可以设置前偏移量和后偏移量,默认的是前偏移量。另外你可以命令vguen在工作日使用date值,不包括

    周六和周日(没有明白这个的具体用处,明白的请告知)。

    2 )、 Group Name    Group Name 用虚拟用户组名称替换参数。在创建 scenario 的时候,你可以指定虚拟用户组的名称。当从用户脚

    本生成器运行脚本的时候,虚拟用户组名称总是 None 。
    char *c =" {NewParam}"; //%05s
    char *d="{NewParam_1}";//%07s
    lr_log_message("group(5s) is %s,group(7s)is %s",lr_eval_string (c),lr_eval_string (d));
    显示的结果为:group(5s)is 0None,group(7s)is 000None
    3). Iteration Number    Iteration Number 用当前的迭代数目替换参数
    设置迭代次数为3
    char *c =" {NewParam}"; //%05s
    char *d="{NewParam_1}";//%07s
    lr_log_message("Iteration(5s) is %s,Iteration(7s)is %s",lr_eval_string (c),lr_eval_string (d));
    结果为:Iteration(5s) is 00001,Iteration(7s)is 0000001
    Iteration(5s) is 00002,Iteration(7s)is 0000002
    Iteration(5s) is 00003,Iteration(7s)is 0000003
    4)、 Load Generator Name    Load Generator Name 用脚本负载生成器的名称替换参数。负载生成器是虚拟用户在运行的计算机。
    5) 、 Random Number    Random Number 用一个随机数替换参数。通过指定最大值和最小值来设置随机数的范围。
    6)、 Unique Number    Unique Number 用一个唯一的数字来替换参数。你可以指定一个起始数字和一个块的大小。
    7 、 Vuser ID    Vuser ID 用分配给虚拟用户的 ID 替换参数, ID 是由 Loadrunner 的控制器在 scenario 运行时生成的。如果你从

    脚本生成器运行脚本的话,虚拟用户的 ID 总是 -1
    8、User-Defined Functions ―― 调用外部 DLL 函数生成的数据 ,函数必须是如下格式:
    __declspec(dllexport) char *<functionName>(char *, char *) 例如:__declspec(dllexport) char *UF_GetVersion(char *x1, char

    *x2) {return "Ver2.0";}

    9、table或file 从已存在的数据库中导入文件
    可以使用下列两种方式之一:
    1. 使用 Microsoft Query (要求在系统上先安装 MS Query )。
    2. 指定数据库连接字符串和 SQL 语句。
    用户脚本生成器在从数据库中导入数据的过程中提供了一个向导。在向导中,你指明如何导入数据-通过 MS Query 创建查询语句或者直接

    书写 SQL 语句。在导入数据以后,以 .dat 为后缀并作为正规的参数文件保存。
    要开始导入数据库中数据的过程,在参数属性对话框中点击“ Data Wizard ”,则,数据库查询向导弹出。
    要创建新的查询
    1. 选择“ Create new query ”。如果需要 MS Query 的帮助,选择“ Show me how to use Microsoft Query ”,然后点击“ Finish ”


    如果你还没有安装 Microsoft Query , Loadrunner 会提示你这个功能不可用。在进行之前,从 Microsoft Office 中安装 MS Query 。
    2. 在 Microsoft Query 中遵循以下步骤,导入期望的表和列。
    3. 在完成数据的导入后,选择“ Exit and return to Virtual User Generator ”,然后点击“ Finish ”。在参数属性对话框中数据库

    记录以 data 文件的形式显示出来。
    要在 MS Query 中编辑并查看数据,选择“ View data or edit in Microsoft Query ”。若要结束,则选择“ File>Exit and return to

    Virtual User Generator ”返回到脚本生成器。
    4. 在“ Select Column ”部分,指定包含当前参数数据的列可以指定列号或者列名。注意:列标题默认为第 0 行( row 0 )。
    5. 从“ Select next row ”列表中选择一个更新方法来告诉虚拟用户在脚本指定的过程中如何选择表中的数据。可选项是: Sequential

    、 Random 、 Unique 或者 Same Line As 。
    6. 如果选择“ Advance row each iteration ”,虚拟用户在每次迭代的时候会使用新的一行的数据而不是重复同样的数据。
    要指定数据库连接或者 SQL 语句
    1. 选择“ Specify SQL Statement ”,然后点击“ Next ”。
    2. 点击“ Create ”指定一个新的连接字符串。选择数据源的窗口弹出。
    3. 选择已有的数据源,或者点击“ New ”创建一个新的数据源。向导将提示你穿过创建 ODBC 数据源的过程。在完成后,连接字符串就会

    在连接字符串框中显示出来。
    4. 在 SQL 框中,输入或者粘贴 SQL 语句。
    5. 点击“ Finish ”继续 SQL 语句并导入数据。数据库记录将以 data 文件的形式显示在参数属性框中。
    6. 在“ Select Column ”部分中,指定包含当前参数数据的列。你可以指定列号或者列名。
    7. 从“ Select next row ”列表中选择一个更新方法来告诉虚拟用户在脚本指定的过程中如何选择表中的数据。可选项是: Sequential

    、 Random 、 Unique 或者 Same Line As 。
    8. 如果选择“ Advance row each iteration ”,虚拟用户在每次迭代的时候会使用新的一行的数据而不是重复同样的数据。
    数据文件   数据文件包含着脚本执行过程中虚拟用户访问的数据。局部和全局文件中都可以存储数据。可以指定现有的 ASCII 文件、用

    脚本生成器创建一个新的文件或者引入一个数据库。在参数有很多已知值的时候数据文件非常有用。数据文件中的数据是以表的形式存储的

    。一个文件中可以包含很多参数值。每一列包含一个参数的数据。列之间用分隔符隔开,比如说,用逗号。  对数据文件设置参数属性 

     如果使用文件作为参数的数据源,必须指定以下内容:文件的名称和位置、包含数据的列、文件格式,包括列的分隔符、更新方法。  

    如果参数的类型是“ File” ,打开参数属性( Parameter Properties )对话框,设置文件属性如下:
    1 、 在 “File path” 中输入文件的位置,或者点击 “Browse” 指定一个已有文件的位置。缺省情况下,所有新的数据文件名都是

    “parameter_name.dat” ,注意,已有的数据文件的后缀必须是 .dat 。
    2 、 点击 “Edit” 。记事本打开,里面第一行是参数的名称,第二行是参数的初始值。使用诸如逗号之类的分隔符将列隔开。对于每一新

    的表行开始一行新的数据。  注意:在没有启动记事本的情况下如果想添加列,就在参数属性对话框中点击“ Add Col” ,那么 “Add

    new column” 对话框就会弹出。输入新列的名称,点击 “OK” 。脚本生成器就会添加该列到表中,并显示该列的初始值。
    3 、 在 “Select Column” 部分,指明包含当前参数数据的列。你可以指定列名或者列号。列号是包含你所需要数据的列的索引。列名显

    示在每列的第一行( row 0 )。
    4 、 在 “Column delimiter” 中输入列分隔符,你可以指定逗号、空格符等等。
    5 、 在 “First data line” 中,在脚本执行的时候选择第一行数据使用。列标题是第 0 行。若从列标题后面的第一行开始的话,那就在

    “First data line” 中输入 1 。如果没有列标题,就输入 0 。
    6 、 在 “Select next row” 中输入更新方法,以说明虚拟用户在脚本执行的过程中如何选择表中的数据。方法可以是:连续的、随机的

    、唯一的、或者与其它参数表的相同行。
    6.1 、 顺序( Sequential ):该方法顺序地给虚拟用户分配参数值。如果正在运行的虚拟用户访问数据表的时候,它会取到下一行中可用

    的数据。
    6.2 、 随机( Random ):该方法在每次迭代的时候会从数据表中取随机数
    6.3 、 使用种子取随机顺序( Use Random Sequence with Seed ):如果从 Loadrunner 的控制器来运行 scenario ,你可以指定一个种

    子数值用于随机顺序。每一个种子数值在测试执行的时候代表了一个随机数的顺序。无论你何时使用这个种子数值,在 scenario 中同样的

    数据顺序就被分配给虚拟用户。如果在测试执行的时候发现了一个问题并且企图使用同样的随机数序列来重复测试,那么,你就可以启动这

    个功能(可选项)。
    6.4 、 唯一( Unique ): Unique 方法分配一个唯一的有顺序的值给每个虚拟用户的参数。
    6.5 、与以前定义的参数取同一行( Same Line As ):该方法从和以前定义过的参数中的同样的一行分配数据。你必须指定包含有该数据

    的列。在下拉列表中会出现定义过的所有参数列表。注意:至少其中的一个参数必须是 Sequential 、 Random 或者 Unique 。
    如果数据表中有三列,三个参数定义在列表中: id1 , name1 和 title1 ,如下:。
    ID Name Title
    132 Kim Manager
    187 Cassie Engineer
    189 Jane VP
    对于参数 id1 ,你可以指示虚拟用户使用 Random 方法,而为参数 name1 和 title1 就可以指定方法 “Same Line as id1” 。所以,一

    旦 ID“132” 被使用,那么,姓名( Name ) “Kim” 和职位( Title ) “Manager” 同时被使用。
    7 、 Updta value on 数据的更新方法
    7.1 、 Each iteration ――每次反复都要取新值。
    7.2 、 Each occurrence ――只要发现该参数就要重新取值。
    7.3 、 Once ――在所有的反复中都使用同一个值
    8 、 When out of values 超出范围:(选择数据为 unique 时才可用到)
    8.1 、 Abort Vuser ――中止
    8.2 、 Continue in a cyclic manner ――继续循环取值
    8.3 、 Continue with last value ――取最后一个值
    9 、 Allocate Vuser values in the Controller 在控制器中分配值:(选择数据为 unique 时才可用到)

  • LoadRunner函数中文翻译系列之一--Action

    2011-04-27 10:24:03

    web_url

     语法:
     Int Web_url(const char *name, const char * url, <Lists of Attributes>, [EXTRARES,<Lists of Resource Attributes>,LAST)
    返回值
     成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)。
    参数:
     Name:VuGen中树形视图中显示的名称,在自动事务处理中也可以用做事务的名称。
    url:页面url地址。
    List of Attributes
    EXTRARES:分隔符,标记下一个参数是资源属性的列表了。
    List of Resource Attributes
    LAST:属性列表结束的标记符。
    说明
     Web_url根据函数中的URL属性加载对应的URL,不需要上下文。
    只有VuGen处于URL-based或者HTML-based(此时A script. containing explicit URLs only选项被选中时)的录制模式时,web_url才会被录制到。
    可以使用web_url 模拟从FTP服务器上下载文件。web_url 函数会使FTP服务器执行文件被真实下载时的操作。除非手工指定了"FtpAscii=1",下载会以二进制模式完成。
    在录制选项中,Toos—Recording Option下,Recording选项中,有一个Advanced HTML选项,可以设置是否录制非HTML资源,只有选择了“Record within the current script. step”时,List of Resource Attributes才会被录制到。非HTML资源的例子是gif和jpg图象文件。
    通过修改HTTP头可以传递给服务器一些附加的请求信息。使用HTTP头允许请求中包含其他的内容类型(Content_type),象压缩文件一样。还可以只请求特定状态下的web页面。
    所有的Web Vusers ,HTTP模式下的WAP Vusers或者回放模式下的Wireless Session Protocol(WSP),都支持web_url函数。
    web_image

     语法:
     Int web_image (const char *StepName, <List of Attributes>, [EXTRARES, <List of Resource Attributes>,] LAST );
    返回值
     成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)。
    参数:
     StepName:VuGen中树形视图中显示的名称,在自动事务处理中也可以用做事务的名称。
    List of Attributes(服务器端和客户端映射的图片):SRC属性是一定会被录制到的,其他的ALT、Frame、TargetFrame、Ordinal则是有的话会被录制到。
    1、ALT:描述图象的元素。用鼠标指向图象时,所浮出来的文字提示。
    2、SRC:描述图象的元素,可以是图象的文件名. 如: button.gif。也可以使用SRC/SFX来指定图象路径的后缀。所有拥有相同此后缀的字符串都会被匹配到。
    3、Frame:录制操作时所在的Frame的名称。
    4、TargetFrame:见List of Attributes的同名参数。
    5、Ordinal:参见Web_link的同名参数。
    List of Attributes(客户端映射的图片):
    1、AreaAlt:鼠标单击区域的ALT属性。
    2、AreaOrdinal:鼠标单击区域的顺序号。
    3、MapName:图象的映射名。
    List of Attributes(服务器端映射的图片):尽管点击坐标不属于属性,但还是以属性的格式来使用。
    1、Xcoord:点击图象时的X坐标。
    2、Ycoord:点击图象时的Y坐标。
    EXTRARES:分隔符,标记下一个参数是资源属性的列表了。
    List of Resource Attributes:参见List of Resource Attributes一节。
    LAST:属性列表结束的标记符。
    说明
    web_image模拟鼠标在指定图片上的单击动作。此函数必须在有前置操作的上下文中使用。
    在Toos—Recording Option,如果录制级别设为基于HMTL的录制方式时,web_image才会被录制到。
    web_image支持客户端(client-side)和服务器端server-side的图片映射。
    在录制选项中,Toos—Recording Option下,Recording选项中,有一个Advanced HTML选项,可以设置是否录制非HTML资源,只有选择了“Record within the current script. step”时,List of Resource Attributes才会被录制到。非HTML资源的例子是gif和jpg图象文件。
    通过修改HTTP头可以传递给服务器一些请求附加信息。使用HTTP头允许请求中包含内容,如同压缩文件一样。还可以只请求特定状态的web页面。
    web_image支持Web虚拟用户,不支持WAP虚拟用户。
    例子
     下面的例子模拟用户单击Home图标以回到主页(黑体部分):
    web_url("my_home", "URL=http://my_home/", LAST);
    web_link("Employees", "Text=Employees", LAST);
    web_image("Home.gif", "SRC=../gifs/Buttons/Home.gif", LAST);
    web_link("Library", "Text=Library", LAST);
    web_image("Home.gif", "SRC=../../gifs/buttons/Home.gif", LAST);
    下面的例子模拟用户在客户端映射的图片上单击:
    web_image("dpt_house.gif",
    "Src=../gifs/dpt_house.gif",
    "MapName=dpt_house",
    "AreaOrdinal=4",
    LAST);
    下面的例子模拟用户在服务端映射的图片上单击:
    web_image("The Web Developer's Virtual Library",
    "Alt=The Web Developer's Virtual Library",
    "Ordinal=1",
    "XCoord=91",
    "YCoord=17",
    LAST);
    下面是一个使用文件名后缀的例子:它指定了dpt_house.gif作为后缀,所以象../gifs/dpt_house.gif、/gifs/dpt_house.gif、gifs/dpt_house.gif、/dpt_house.gif等都会匹配到。
    web_image("dpt_house.gif","Src/sfx=dpt_house.gif", LAST);
    web_link
     语法:
     Int web_link (const char *StepName, <List of Attributes>, [EXTRARES, <List of Resource Attributes>,] LAST );
    返回值
     成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)。
    参数:
     StepName:VuGen中树形视图中显示的名称,在自动事务设置中也被用做事务名称。
    List of Attributes:支持下列的属性:
    1. Text:超链接中的文字,必须精确匹配。
    2. Frame:录制操作时所在的Frame的名称。
    3. TargetFrame、ResourceByteLimit:见List of Attributes一节。
    4. Ordinal:如果用给出的属性(Attributes)筛选出的元素不唯一,那么VuGen使用此属性来指定其中的一个。例如:“SRC=abc.gif”,“Ordinal=3”标记的是SRC的值是“abc.gif”的第3张图片。
    EXTRARES:表明下面的参数将会是list of resource attributes了。
    LAST:结尾标示符。
    说明
     模拟鼠标在由若干个属性集合描述的链接上进行单击。此函数必须在前置动作的上下文中才可以执行。
    web_link 仅仅在基于HTML的录制方式中才会被VuGen捕捉到。
    非HTML生成的资源的例子有.gif 和.jpg图像。对于List of Resource Attributes参数来说,仅仅当Recording Options--Recording --HTML-based script-- Record within the current script. step选项被选中时,它们才会被插入到代码中。
    可以通过改变HTTP头信息给服务器传递一些附加信息。使用HTTP头信息可以,允许响应体中包含其他的内容类型(Content-Type),例如压缩文件,或者只有满足了特定的状态才去请求web页。
    此函数值支持Web虚拟用户,不支持WAP虚拟用户。
    web_submmit_form

     语法:
     Int web_submit_form. (const char *StepName, <List of Attributes>, <List of Hidden Fields>, ITEMDATA, <List of Data Fields>, [ EXTRARES, <List of Resource Attributes>,] LAST );
    返回值
     成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)。
    参数:
     StepName:Form的名字。VuGen中树形视图中显示的名称,在自动事务处理中也可以用做事务的名称。
    List of Attributes:支持以下属性:
    1. Action:Form中的ACTION属性,指定了完成Form中的操作用到的URL。也可以使用“Action/sfx” 表示使用此后缀的所有Action。
    2. Frame:录制操作时所在的Frame的名称。
    3. TargetFrame、ResourceByteLimit:见List of Attributes的同名参数。
    4. Ordinal:参见Web_link的同名参数。
    VuGen通过记录数据域唯一的标识每个Form。如果这样不足以识别Form,VuGen会记录Action 属性。如果还不足以识别,则会记录Ordinal 属性,这种情况下不会记录Action属性。
    List of Hidden Fields:补充属性(Serves)。通过此属性可以使用一串隐含域来标识Form。使用下面的格式:
    STARTHIDDENS,
    "name=n1", "value=v1", ENDITEM,
    "name=n2", "value=v2", ENDITEM,
    ENDHIDDENS,
    List of Data Fields
    Data项用来标识form。Form是通过属性和数据来共同识别的。
    使用下面的格式来表示数据域列表
    "name=n1", "value=v1", ENDITEM,
    "name=n2", "value=v2", ENDITEM,
    ITEMDATA:Form中数据和属性的分隔符。
    EXTRARES:一个分隔符,标记下一个参数是资源属性的列表了。
    List of Resource Attributes:参见List of Resource Attributes一节。
    LAST:属性列表结束的标记符。
    说明
     web_submit_form. 函数用来提交表单。此函数可能必须在前一个操作的上下文中执行。在Toos—Recording Option,只有录制级别设为基于HMTL的录制方式,web_image才会被录制到。
    在录制选项中,Toos—Recording Option下,Recording选项中,有一个Advanced HTML选项,可以设置是否录制非HTML资源,只有选择了“Record within the current script. step”时,List of Resource Attributes才会被录制到。非HTML资源的例子是gif和jpg图象文件。
    通常情况下,如果录制了web_submit_form. 函数,VuGen会把“name”和“value”一起录制到ITEMDATA属性中。如果不想在脚本中以明文显示“value”,可以对它进行加密。把“Value”改为“EncryptedValue”,然后把录制到的值改为加密后的值。
    例如:可以把 "Name=grpType", "Value=radRoundtrip", ENDITEM
    改为:"Name=grpType", EncryptedValue=409e41ebf102f3036b0549c799be3609", ENDITEM
    如果你完整的安装了LoadRunner,那么打开开始菜单--Mercury LoadRunner—Tools--Password Encoder,这个小工具是用来加密字符串的。把需要加密的值粘贴到Password一栏,再点Generate按钮。加密后的字符串会出现在Encoded string框中。接着点Copy按钮,然后把它粘贴到脚本中,覆盖原来显示的“Value”。
    加密的另一种方法时使用lr_decrypt函数。方法:选择整个字符串,例如“Value=radRoundtrip”(注意不要选择引号),右击鼠标,选择Encrypt string选现,脚本会变为:
    "Name=grpType", lr_decrypt("40d176c46f3cf2f5fbfaa806bd1bcee65f0371858163"), ENDITEM,
    web_submit_form支持Web虚拟用户,不支持WAP虚拟用户。
    例子:
     下面的例子中,web_submit_form. 函数的名字是“employee.exe”。此函数提交了一个请求,此请求包含雇员信息John Green。此函数没有使用属性(Attributes)是因为通过数据项已经能唯一的标识这个Form了。
    web_submit_form("employee.exe",
    ITEMDATA,
    "name=persons", "value=John Green - John", ENDITEM,
    "name=go_page", "value=Go to Page", ENDITEM,
    LAST);
    web_submmit_data

     语法:
     Int web_submit_data ( const char *StepName, <List of Attributes>, ITEMDATA, <List of data>, [ EXTRARES, <List of Resource Attributes>,] LAST );
    返回值
     返回LR_PASS(0)代表成功,LR_FAIL(1)代表失败。
    参数:
     StepName:步骤名称,VuGen中树形视图显示的名称。
    List of Attributes:支持以下属性:
    1. Action:Form中的ACTION属性,指定了完成Form中的操作用到的URL。
    2. Method:表单提交方法:POST或GET(默认是POST)。
    3. EncType:编码方式。
    4. EncodeAtSign:是否使用ASCII值对符号“@”编码。Yes或者 No。
    5. TargetFrame:包含当前链接或资源的Frame。参见List of Attributes的同名参数。
    6. Referer、Mode:参见List of Attributes的同名参数。
    ITEMDATA:数据域和属性的分隔符。
    List of Data:
    数据域列表定义了表单提交的内容。由于此请求是上下文无关的,因此数据域包含了所有的隐含域。使用Form的编码规则组织数据域。
    数据域列表可以使用下面任意一种格式:
    "name=n1", "value=v1", ENDITEM,
    "name=n2", "EncryptedValue=qwerty", ENDITEM,
    EXTRARES:分隔符,标记下一个参数将是资源属性的列表。
    List of Resource Attributes:参见List of Resource Attributes。
    LAST:结束标记符。
    说明
     web_submit_data函数处理无状态或者上下文无关的表单提交。它用来生成表单的GET或POST请求,这些请求与Form自动生成的请求是一样的。发送这些请求时不需要表单上下文。
    当VuGen设为基于URL的录制模式,或者基于HTML的录制方式但是Recording Options—HTML Advanced 下的A script. containing explicit URLs only 选项被选中时,web_submmit_data函数才会录制到。
    不论你采用URL查询的方式(GET),还是采用请求体发送(POST)的方式,此函数都指示出Form中的数据是如何发送到服务器的。
    如果VuGen处于HTTP录制模式下,此时记录Web进程时,会产生此函数。在提交Form时,如果无法生成web_submit_form函数,VuGen也会生成web_submit_data函数。
    在录制选项中,Toos—Recording Option下—Recording选项中,有一个Advanced HTML选项,可以设置是否录制非HTML资源。只有选择了“Record within the current script. step”时,List of Resource Attributes才会被录制到。非HTML资源的例子是gif和jpg图象文件。
    EncType属性给出一个内容类型,指定其做为“Content-Type”请求头的值。它指示了根据参数生成HTTP请求时使用的编码类型(不是URL-encoding就是multi-part)可以是下面的格式:
    1. “EncType=application/x-www-form-urlencoded”
    2. “EncType=multipart/form-data” (任何的“; boundary=”都会被默认忽略掉)
    3. “EncType=” (空串,表明没有产生内容类型(“Content-Type”)请求头)
    任何对于“EncType”的指定都会覆盖web_add_[auto_]header函数指定的Content-Type。当省略了“EncType”时,任何一个web_add_[auto_]header函数都会起作用。如果既没有指定EncType也没有web_add_[auto_]header函数,且“Method=POST”,“application/x-www-form-urlencoded”会做为默认值来使用。其他情况下,不会产生Content-Type请求头。
    ContentType:文件类型标识符,如果“EncType”是“multipart/form-data”用来上传文件时,需要用到“ContentType”。当在ITEMDATA中的Data子句中指定了“File=Yes”,且文件也在此子句中,ContentType才适用,此时它会作为同一个子句的值来传递。
    正常情况下,“Content-Type”根据所上传文件的扩展名自动生成。例如:
    7d025e2b16b064e\r\n Content-Disposition: form-data; name="uploaded_file"; filename="D:\\temp\\a.txt"\r\n Content-Type: text/plain\r\n \r\n
    无论如何,对于非浏览器的程序来说是特殊的,根据文件类型生成的“ContentType”不一定是正确的。这时,通过手工指定来覆盖默认的“ContentType”。如果指定了空值,那么“Content-Type”头将不包含在文件中。
    如果没有显示的指定“ContentType”的值,当上传的文件为空时,不管文件扩展名是什么,都默认使用“application/x-unknown-content-type”做为”ContentType”的值。
    VuGen不会检查指定的ContentType是否有效。
    通常情况下,如果录制了web_submit_data 函数,VuGen会把“name”和“value”一起录制到ITEMDATA一节中。如果不想在脚本中以明文显示“value”,可以对它进行加密。把“Value”改为“EncryptedValue”,然后把录制到的值改为加密后的值。请参考web_submit_form中相关的内容。
    所有的Web虚拟用户,运行在HTTP模式下的WAP用户,运行在WSP回放模式下的WAP用户都可以使用本函数。
    例子
     下面的例子中,web_submit_data函数使用POST方法提交了一个表单。
    web_submit_data("default.aspx",
    "Action=http://lazarus/flightnet/default.aspx",
    "Method=POST",
    "TargetFrame=",
    "RecContentType=text/html",
    "Referer=http://lazarus/flightnet/",
    "Snapshot=t7.inf",
    "Mode=HTML",
    ITEMDATA,
    "Name=grpType", "Value=radRoundtrip", ENDITEM,
    "Name=lstDepartingCity", "Value=DEN", ENDITEM,
    "Name=lstDestinationCity", "Value=LAX", ENDITEM,
    "Name=txtDepartureDate", "Value=8/19/2003", ENDITEM,
    "Name=txtReturnDate", "Value=8/19/2003", ENDITEM,
    "Name=txtQuantity", "Value=1", ENDITEM,
    "Name=radClass", "Value=1", ENDITEM,
    "Name=radSeat", "Value=1", ENDITEM,
    "Name=btnAvailableFlights", "Value=Next >", ENDITEM,
    LAST);
    下面的例子, web_submit_data函数使用POST方法提交了2个文件。
    web_submit_data("Attachments",
    "Action=http://barton.cottage@.Devonshire.uk/Attachments?YY=45434",
    "Method=POST",
    "EncType=multipart/form-data",
    "TargetFrame=",
    "RecContentType=text/html",
    "Referer=http:///barton.cottage@.Devonshire.uk/Compose?YY=20435",
    "Snapshot=t5.inf",
    "Mode=HTML",
    ITEMDATA, "Name=userFile0",
    "Value=E:\\sense_sensibility\\Elinor.txt",
    "File=yes",
    "ContentType=text/html", // 覆盖了文本文件默认的“text/plain” 值。
    ENDITEM,
    "Name=userFile1",
    "Value=E:\\sense_sensibility\\Marianne.jpg",
    "File=yes",
    ENDITEM,
    LAST);
    web_custom_request

     语法:
     Int web_custom_request (const char *RequestName, <List of Attributes>,
    [EXTRARES, <List of Resource Attributes>,] LAST );
    返回值
     返回LR_PASS(0)代表成功,LR_FAIL(1)代表失败。
    参数:
     RequestName:步骤的名称,VuGen中树形视图中显示的名称。
    List of Attribute:支持的属性有以下几种:
    1. URL:页面地址。
    2. Method :页面的提交方式,POST或GET。
    3. TargetFrame:包含当前链接或资源的frame的名称。参见List of Attributes的同名参数。
    4. EncType:编码类型。
    5. RecContentType:响应头的内容类型。参见List of Attributes的同名参数。
    6. Referer:参见List of Attributes的同名参数。
    7. Body:请求体。参见List of Attributes的同名参数。
    8. RAW BODY:参见List of Attributes的同名参数。
    9. BodyFilePath:作为请求体传送的文件的路径。它不能与下面的属性一起使用:Body,或者其他Body属性或Raw Body属性包括BodyBinary,BodyUnicode, RAW_BODY_START或Binary=1。
    10. Resource、ResourceByteLimit、Snapshot、Mode:参见List of Attributes的同名参数。
    11. ExtraResBaseDir:参见List of Attributes的同名参数。
    12. UserAgent:用户代理,它是一个HTTP头的名字,用来标识应用程序,通常是浏览器,它呈现的是用户和服务器的交互。
    例如:头信息“User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)”识别的是Window NT下的IE浏览器6.0。其他的User-Agent的值用来描述其他的浏览器,或者非浏览器程序。通常,一个应用程序中所有的请求都使用相同的用户代理,录制者作为一个运行时参数来指定(Run-Time Setting—Browser Emulation—User Agent)。不管怎么说,即使是在一个简单的浏览器进程中,仍有可能会用到直接与服务器交互的非浏览器组件(例如ActiveX控件),通常他们有着不同于浏览器的用户代理属性。指定“UserAgent”表示这是一个非浏览器的请求。指定的字符串被HTTP头“User-Agent:” 使用,在某些情况下,它同时会影响回放脚本时的行为。例如,不使用浏览器缓存,假设指定的URL属于资源等等。
    LoadRunner本身不检查指定的字符串与浏览器本身的值是否相同。
    13. Binary:“Binary=1”表示页面请求体中的每一个以\\x##形式出现的值(在这里“##”代表2个十六进制数字),都会被替换为单字节的十六进制的值。
    如果“Binary=0”(默认值),所有的字符序列只是按照字面的值传递。
    需要注意双斜杠的用法。在C编译器中双斜杠被解释为单斜杠。如果不需要零字节,单斜杠可以在Binary不等于1的情况下使用(例如,使用\x20代替\\x20)。如果需要零字节,那么只能使用\\x00且设置 “Binary=1”,\x00在逻辑上会被截断。

  • Loadrunner函数中文解释

    2011-04-27 10:20:53

    web_url

    语法:
    Int Web_url(const char *name, const char * url, <Lists of Attributes>, [EXTRARES,<Lists of Resource Attributes>,LAST)

    返回值
    成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)。

    参数:
    Name:VuGen中树形视图中显示的名称,在自动事务处理中也可以用做事务的名称。

    url:页面url地址。

    List of Attributes

    EXTRARES:分隔符,标记下一个参数是资源属性的列表了。

    List of Resource Attributes

    LAST:属性列表结束的标记符。


    说明

    Web_url根据函数中的URL属性加载对应的URL,不需要上下文。

    只有VuGen处于URL-based或者HTML-based(此时A scrīpt containing explicit URLs only选项被选中时)的录制模式时,web_url才会被录制到。

    可以使用web_url 模拟从FTP服务器上下载文件。web_url 函数会使FTP服务器执行文件被真实下载时的操作。除非手工指定了"FtpAscii=1",下载会以二进制模式完成。

    在录制选项中,Toos—Recording Option下,Recording选项中,有一个Advanced HTML选项,可以设置是否录制非HTML资源,只有选择了“Record within the current scrīpt step”时,List of Resource Attributes才会被录制到。非HTML资源的例子是gif和jpg图象文件。

    通过修改HTTP头可以传递给服务器一些附加的请求信息。使用HTTP头允许请求中包含其他的内容类型(Content_type),象压缩文件一样。还可以只请求特定状态下的web页面。

    所有的Web Vusers ,HTTP模式下的WAP Vusers或者回放模式下的Wireless Session Protocol(WSP),都支持web_url函数。


    web_image

    语法:
    Int web_image (const char *StepName, <List of Attributes>, [EXTRARES, <List of Resource Attributes>,] LAST );

    返回值
    成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)。

    参数:
    StepName:VuGen中树形视图中显示的名称,在自动事务处理中也可以用做事务的名称。

    List of Attributes(服务器端和客户端映射的图片):SRC属性是一定会被录制到的,其他的ALT、Frame、TargetFrame、Ordinal则是有的话会被录制到。

    1、ALT:描述图象的元素。用鼠标指向图象时,所浮出来的文字提示。

    2、SRC:描述图象的元素,可以是图象的文件名. 如: button.gif。也可以使用SRC/SFX来指定图象路径的后缀。所有拥有相同此后缀的字符串都会被匹配到。

    3、Frame:录制操作时所在的Frame的名称。

    4、TargetFrame:见List of Attributes的同名参数。

    5、Ordinal:参见Web_link的同名参数。

    List of Attributes(客户端映射的图片):

    1、AreaAlt:鼠标单击区域的ALT属性。

    2、AreaOrdinal:鼠标单击区域的顺序号。

    3、MapName:图象的映射名。

    List of Attributes(服务器端映射的图片):尽管点击坐标不属于属性,但还是以属性的格式来使用。

    1、Xcoord:点击图象时的X坐标。

    2、Ycoord:点击图象时的Y坐标。

    EXTRARES:分隔符,标记下一个参数是资源属性的列表了。

    List of Resource Attributes:参见List of Resource Attributes一节。

    LAST:属性列表结束的标记符。

    说明

    web_image模拟鼠标在指定图片上的单击动作。此函数必须在有前置操作的上下文中使用。

    在Toos—Recording Option,如果录制级别设为基于HMTL的录制方式时,web_image才会被录制到。

    web_image支持客户端(client-side)和服务器端server-side的图片映射。

    在录制选项中,Toos—Recording Option下,Recording选项中,有一个Advanced HTML选项,可以设置是否录制非HTML资源,只有选择了“Record within the current scrīpt step”时,List of Resource Attributes才会被录制到。非HTML资源的例子是gif和jpg图象文件。

    通过修改HTTP头可以传递给服务器一些请求附加信息。使用HTTP头允许请求中包含内容,如同压缩文件一样。还可以只请求特定状态的web页面。

    web_image支持Web虚拟用户,不支持WAP虚拟用户。

    例子

    下面的例子模拟用户单击Home图标以回到主页(黑体部分):

    web_url("my_home", "URL=http://my_home/", LAST);

    web_link("Employees", "Text=Employees", LAST);

    web_image("Home.gif", "SRC=../gifs/Buttons/Home.gif", LAST);

    web_link("Library", "Text=Library", LAST);

    web_image("Home.gif", "SRC=../../gifs/buttons/Home.gif", LAST);

    下面的例子模拟用户在客户端映射的图片上单击:

    web_image("dpt_house.gif",


    "Src=../gifs/dpt_house.gif",


    "MapName=dpt_house",


    "AreaOrdinal=4",


    LAST);


    下面的例子模拟用户在服务端映射的图片上单击:

    web_image("The Web Developer's Virtual Library",


    "Alt=The Web Developer's Virtual Library",


    "Ordinal=1",


    "XCoord=91",


    "YCoord=17",


    LAST);

    下面是一个使用文件名后缀的例子:它指定了dpt_house.gif作为后缀,所以象../gifs/dpt_house.gif、/gifs/dpt_house.gif、gifs/dpt_house.gif、/dpt_house.gif等都会匹配到。

    web_image("dpt_house.gif",

    "Src/sfx=dpt_house.gif", LAST);


    web_link

    语法:
    Int web_link (const char *StepName, <List of Attributes>, [EXTRARES, <List of Resource Attributes>,] LAST );

    返回值
    成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)。

    参数:
    StepName:VuGen中树形视图中显示的名称,在自动事务设置中也被用做事务名称。

    List of Attributes:支持下列的属性:

    1.Text:超链接中的文字,必须精确匹配。

    2.Frame:录制操作时所在的Frame的名称。

    3.TargetFrame、ResourceByteLimit:见List of Attributes一节。

    4.Ordinal:如果用给出的属性(Attributes)筛选出的元素不唯一,那么VuGen使用此属性来指定其中的一个。例如:“SRC=abc.gif”,“Ordinal=3”标记的是SRC的值是“abc.gif”的第3张图片。

    EXTRARES:表明下面的参数将会是list of resource attributes了。

    LAST:结尾标示符。

    说明

    模拟鼠标在由若干个属性集合描述的链接上进行单击。此函数必须在前置动作的上下文中才可以执行。

    web_link 仅仅在基于HTML的录制方式中才会被VuGen捕捉到。

    非HTML生成的资源的例子有.gif 和.jpg图像。对于List of Resource Attributes参数来说,仅仅当Recording Options--Recording --HTML-based scrīpt-- Record within the current scrīpt step选项被选中时,它们才会被插入到代码中。

    可以通过改变HTTP头信息给服务器传递一些附加信息。使用HTTP头信息可以,允许响应体中包含其他的内容类型(Content-Type),例如压缩文件,或者只有满足了特定的状态才去请求web页。

    此函数值支持Web虚拟用户,不支持WAP虚拟用户。


    web_submmit_form

    语法:
    Int web_submit_form. (const char *StepName, <List of Attributes>, <List of Hidden Fields>, ITEMDATA, <List of Data Fields>, [ EXTRARES, <List of Resource Attributes>,] LAST );


    返回值
    成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)。


    参数:
    StepName:Form的名字。VuGen中树形视图中显示的名称,在自动事务处理中也可以用做事务的名称。
    List of Attributes:支持以下属性:

    1.Action:Form中的ACTION属性,指定了完成Form中的操作用到的URL。也可以使用“Action/sfx” 表示使用此后缀的所有Action。

    2.Frame:录制操作时所在的Frame的名称。

    3.TargetFrame、ResourceByteLimit:见List of Attributes的同名参数。

    4.Ordinal:参见Web_link的同名参数。


    VuGen通过记录数据域唯一的标识每个Form。如果这样不足以识别Form,VuGen会记录Action 属性。如果还不足以识别,则会记录Ordinal 属性,这种情况下不会记录Action属性。

    List of Hidden Fields:补充属性(Serves)。通过此属性可以使用一串隐含域来标识Form。使用下面的格式:

    STARTHIDDENS,

    "name=n1", "value=v1", ENDITEM,

    "name=n2", "value=v2", ENDITEM,

    ENDHIDDENS,

    List of Data Fields

    Data项用来标识form。Form是通过属性和数据来共同识别的。

    使用下面的格式来表示数据域列表

    "name=n1", "value=v1", ENDITEM,

    "name=n2", "value=v2", ENDITEM,

    ITEMDATA:Form中数据和属性的分隔符。

    EXTRARES:一个分隔符,标记下一个参数是资源属性的列表了。

    List of Resource Attributes:参见List of Resource Attributes一节。

    LAST:属性列表结束的标记符。

    说明

    web_submit_form. 函数用来提交表单。此函数可能必须在前一个操作的上下文中执行。在Toos—Recording Option,只有录制级别设为基于HMTL的录制方式,web_image才会被录制到。

    在录制选项中,Toos—Recording Option下,Recording选项中,有一个Advanced HTML选项,可以设置是否录制非HTML资源,只有选择了“Record within the current scrīpt step”时,List of Resource Attributes才会被录制到。非HTML资源的例子是gif和jpg图象文件。

    通常情况下,如果录制了web_submit_form. 函数,VuGen会把“name”和“value”一起录制到ITEMDATA属性中。如果不想在脚本中以明文显示“value”,可以对它进行加密。把“Value”改为“EncryptedValue”,然后把录制到的值改为加密后的值。

    例如:可以把 "Name=grpType", "Value=radRoundtrip", ENDITEM

    改为:"Name=grpType", EncryptedValue=409e41ebf102f3036b0549c799be3609", ENDITEM

    如果你完整的安装了LoadRunner,那么打开开始菜单--Mercury LoadRunner—Tools--Password Encoder,这个小工具是用来加密字符串的。把需要加密的值粘贴到Password一栏,再点Generate按钮。加密后的字符串会出现在Encoded string框中。接着点Copy按钮,然后把它粘贴到脚本中,覆盖原来显示的“Value”。

    加密的另一种方法时使用lr_decrypt函数。方法:选择整个字符串,例如“Value=radRoundtrip”(注意不要选择引号),右击鼠标,选择Encrypt string选现,脚本会变为:

    "Name=grpType", lr_decrypt("40d176c46f3cf2f5fbfaa806bd1bcee65f0371858163"), ENDITEM,

    web_submit_form支持Web虚拟用户,不支持WAP虚拟用户。

    例子:

    下面的例子中,web_submit_form. 函数的名字是“employee.exe”。此函数提交了一个请求,此请求包含雇员信息John Green。此函数没有使用属性(Attributes)是因为通过数据项已经能唯一的标识这个Form了。

  • LoadRunner脚本编写-- 检查点,关联等函数

    2011-04-27 10:20:23

    1. 错误预防和恢复

    参数默认是用{}括起来的,但也可以指定用<>51Testing软件测试网

    NTLM或用户登录验证

    web_set_user("X\\Y", "Z", "A.com:80");

    在域与X上的用户名为Y的用户,使用密码Z来登录到A.com:80。在windows基本验证的时候这个脚本被默认录制下来,但如果web服务器需要更安全的NTLM或更深层次的验证,需要手动的添加这个函数到脚本中。对于NTML验证,用户名必须在域名之后,并且以\分割。使用\等符号,需要使用\\,前面的\用来做转义用,否则会出现警告提示。

    zibeike注:在论坛中也看到了一些朋友讨论windows弹出登录框的操作LR无法录制到,导致回放出错,一般出错信息多为“Error -26547: Authentication required, please use web_set_user, e.g. web_set_user("domain\\user", "password", "host:port"); [MsgId: MERR-26547]”,其实这种情况错误信息已经很明显的给你提示了,需要往脚本中添加web_set_user函数即可。

    2. IP欺骗(略)

    3. 验证检查点

    通常脚本录制完后需要手动添加些脚本来来确保预期的操作确实进行了正确的响应(如在操作之后后验证显示的一段文本或者图片)。这些检查可以使用正则表达式。

    Web虚拟用户脚本中不会录制到检查点,需要手动添加或者使用VuGen的用户接口来添加函数代码。

    最常用的检查点函数是web_reg_find。这个注册函数会查找脚本中下一个操作如web_url后产生的一段文本。它是从返回的缓冲区扫描而不是在接收的页面中查找。这是比web_find更高效的一个函数。

    可以使用下面的代码来验证文本出现的次数:

    • web_reg_find("Text=ABC", "SaveCount=abc_count", LAST);
    • web_url("Step", "URL=...", LAST);
      if (strcmp(lr__string("{abc_count}"), "0") == 0)

    lr_output_message("not found");

    else

    lr_output_message("{abc_count} found");

    如果想保存并且显示找到的文本,可以使用web_reg_save_param界定左右边界把找到的信息保存到参数中。如下:

    • char *str1,*str2;
    • str1="desired text";
      web_reg_save_param("param","LB/ic=xxx","RB=xxx");
      web_url("some url","URL=www.xxx.com",LAST);
    • str2=lr__string("{param}");
    • if(strcmp(str1,str2)==0) {

    lr_output_message("param found");

    }else{

    lr_output_message("Val found is %s",str2);

    }

    zibeike注:1)这里想跟大家说下注册函数,在web/http协议的脚本中,注册函数均以web_reg为前缀,这种注册型的函数都是从缓冲区扫描或者获得数据,因此需要提前声明即需要在能获得该查找信息的函数之前添加这些注册函数。例如,web_url()请求了一个页面,我需要验证该页面中是否有某个特定的文本,那需要在web_url()函数之前加上web_reg_find,类似的还有关联的函数web_reg_save_para是一样的,需要放到能获得想要的数据的请求的函数之前。但如果想查看这些函数最终保存的结果,如想打印关联函数web_reg_save_para中保存的参数内容,打印的操作就需要放到请求的函数之后了。

    2)web_find和web_reg_find的区别:前面的是查找页面显示的数据,因此需要放在请求页面的函数之后,而且查找的信息是显示的web页面上的信息。后者是注册型函数,需要放到请求的页面之前,而且查找的内容是服务器返回的缓冲数据中查找,所以查找内容应该看html源代码的内容。

    基于HTML录制方式的代码,可以使用web_image_check对HTML页面中包含的图片进行验证。并且需要注意的是只有在Runtime Settings > Internet Protocol Preferences选择了"Enable Image and text check"检查点才有效。

  • web_find()和web_reg_find()

    2011-04-27 10:18:41

    这两个函数均用于内容的查找,但两者也有本质的区别,具体介绍如下:

    一、web_find()函数

    该函数的作用是“在页面中查找相应的内容”,常用参数及含义如下:

    web_find("web_find", //定义该查找函数的名称
    %vR`}1}0 "RightOf=a", //定义查找字符的右边界

    "LeftOf=b", //定义查找字符的左边界

    "What=name", //定义查找内容

    LAST);

    使用该函数注意以下事项:

    1、 位置

    该函数在页面内容显示出来以后,在页面中进行查找,所以只能写在要查找内容之后

    2、 录制模式

    该函数只能在基于HTML模式录制的脚本中进行查找

    3、 必须启用内容检查选项

    在runtime setting->Preferences里面,把Enable image and text check选中,否则不执行该查找函数

    4、 在VB和JAVA语法中不支持该函数

    该函数有以下一个缺点:

    1、 执行效率较低

    2、 不返回查找结果情况,如想在执行该函数后根据查找结果做进一步操作时,没有返回值可以依据

    例如:

    在页面中查找“登录成功”的字符串,如果找到该字符串在日志中输出“登录成功”,如果找不到该字符串,则在日志中输出“登录失败”,此时使用该函数没有依据来做此判断,但使用web_reg_find()函数,使用它其中的SaveCount可以进行判断,具体方法我们下面介绍。

    转载请注明出处:http://www.51testing.com/?41972

    二、web_reg_find()函数

    该函数的作用是“在缓存中查找相应的内容”,常用参数及含义如下:

    web_reg_find("Search=Body", //定义查找范围

    "SaveCount=ddd", //定义查找计数变量名称

    "Text=aaaa", //定义查找内容

    LAST);

    使用该函数注意以下事项:

    1、 位置

    该函数写在要查找内容的请求之前,通常情况下写在如下六个函数之前:

    Web_castom_request();web_image();web_link();web_submit_data();web_submit_form();web_url()

    2、 使用技巧

    在该函数的参数中有个“SaveCount”,该参数可以记录在缓存中查找内容出现的次数,我们可以使用该值,来判断要查找的内容是否被找到,下面举个例子来说明:(引用LR的帮助中的例子)

    // Run the Web Tours sample

    web_url("MercuryWebTours",

    "URL=http://localhost/MercuryWebTours/",

    "Resource=0",

    "RecContentType=text/html",

    "Referer=",

    "Snapshot=t1.inf",

    "Mode=HTML",

    LAST);

    // Set up check for successful login by looking for "Welcome"

    web_reg_find("Text=Welcome",

    "SaveCount=Welcome_Count",

    LAST);

    // Now log in

    web_submit_form("login.pl",

    "Snapshot=t2.inf",

    ITEMDATA,

    "Name=username", "Value=jojo", ENDITEM,

    "Name=password", "Value=bean", ENDITEM,

    "Name=login.x", "Value=35", ENDITEM,

    "Name=login.y", "Value=14", ENDITEM,

    LAST);

    // Check result

    if (atoi(lr__string("{Welcome_Count}")) > 0){ //判断如果Welcome字符串出现次数大于0

    lr_output_message("Log on successful."); }//在日志中输出Log on successful

    else{ //如果出现次数小于等于

    lr_error_message("Log on failed"); //在日志中输出Log on failed

    return(0); }

    我觉得这个方法非常有用,我们可以举一反三,应用到我们实际的项目中

    转载请注明出处:http://www.51testing.com/?41972

    三、插入函数的方法

    1、 手工写入,在需要插入函数的位置手工写入该函数

    2、 光标停留在要插入函数的位置,在INSERT菜单中,选择new step,在列表中选择或查找要插入的函数,根据提示填写必要的参数

    3、 在tree view模式下,在树状菜单中选中要插入函数的位置,右键,选择insert after或insert before,根据提示填写必要的参数

    四、总结

    1、 这两个函数函数类型不同,WEB_FIND是普通函数,WEB_REG_FIND是注册函数

    2、 WEB_FIND使用时必须开启内容检查选项,而WEB_REG_FIND则不没有此限制

    3、 WEB_FIND只能只用在基于HTML模式录制的脚本中,而WEB_REG_FIND没有此限制

    4、 WEB_FIND是在返回的页面中进行内容查找,WEB_REG_FIND是在缓存中进行查找

    5、 WEB_FIND在执行效率上不如WEB_REG_FIND
  • LoadRunner脚本实例来验证参数化的取值

    2011-04-27 10:16:58

    问题提出: 主要想试验下,在Controller中,多个用户,多次迭代中参数的取值.


    方法:

    脚本: 我把取到的参数值和对应的VuserID记录下来保存到一个文件中,下面是例子的脚本

    long fileopen;

    char *filename = "C:\\temp\\params.log";


    vuser_init()

    {

    if ((fileopen = fopen(filename,"a+")) == NULL)

    {

    lr_error_message ("file isn't open,path=%s",filename);

    return 0;
    }
    return 0;
    }


    Action()

    {
    int id,scid;

    char *vuser_group;

    //typedef long time_t;

    //time_t t;


    long t;

    char *a = "{aaa}";

    char *b = "{bbb}";

    lr_whoami(&id, &vuser_group, &scid);

    fprintf(fileopen,"%d,%s,%s,%s",id,lr__string (a),lr__string (b),ctime(&t));

    return 0;
    }

    vuser_end()
    {
    fclose(fileopen);

    return 0;

    }


    参数设置为:

    取唯一值,每次迭代更新。

    运行时设置的迭代次数为2。


    场景的设置:

    虚拟用户数:5个

    运行模式:没有设置duration,选择的“Run until complete”

    完成后的日志文件params.log的内容如下:

    1,a1,b1,Thu Jan 01 08:00:05 1970
    1,a2,b2,Thu Jan 01 08:00:05 1970
    10,a5,b5,Thu Jan 01 08:00:05 1970
    10,a6,b6,Thu Jan 01 08:00:05 1970
    2,a3,b3,Thu Jan 01 08:00:05 1970
    2,a4,b4,Thu Jan 01 08:00:05 1970
    30,a9,b9,Thu Jan 01 08:00:05 1970
    30,a10,b10,Thu Jan 01 08:00:05 1970
    21,a7,b7,Thu Jan 01 08:00:05 1970
    21,a8,b8,Thu Jan 01 08:00:05 1970

    从生成的日志文件中可以看到VuserID分别为1,2,10,21,30

    第一次迭代取参数分别为1,3,5,7,9

    第二次迭代取参数分别为2,4,6,8,10

    那试验的结果就是每个Vuser取参数的时候相同的Vuser在多个迭代中取的是连续的。不同的Vuser第一个的取值是计算了迭代次数后的那个顺序上的值。

  • LoadRunner函数小全

    2011-04-27 10:16:09

    给出一部分常用的LoadRunner函数,供大家参考。

    LR函数:

    lr_start_transaction

    为性能分析标记事务的开始

    lr_end_transaction

    为性能分析标记事务的结束

    lr_rendezvous

    在 Vuser 脚本中设置集合点

    lr_think_time

    暂停 Vuser 脚本中命令之间的执行

    lr_end_sub_transaction

    标记子事务的结束以便进行性能分析

    lr_end_transaction

    标记 LoadRunner 事务的结束

    Lr_end_transaction("trans1",Lr_auto);

    lr_end_transaction_instance

    标记事务实例的结束以便进行性能分析

    lr_fail_trans_with_error

    将打开事务的状态设置为 LR_FAIL 并发送错误消息

    lr_get_trans_instance_duration

    获取事务实例的持续时间(由它的句柄指定)

    lr_get_trans_instance_wasted_time

    获取事务实例浪费的时间(由它的句柄指定)

    lr_get_transaction_duration

    获取事务的持续时间(按事务的名称)

    lr_get_transaction_think_time

    获取事务的思考时间(按事务的名称)

    lr_get_transaction_wasted_time

    获取事务浪费的时间(按事务的名称)

    lr_resume_transaction

    继续收集事务数据以便进行性能分析

    lr_resume_transaction_instance

    继续收集事务实例数据以便进行性能分析

    lr_set_transaction_instance_status

    设置事务实例的状态

    lr_set_transaction_status

    设置打开事务的状态

    lr_set_transaction_status_by_name

    设置事务的状态

    lr_start_sub_transaction

    标记子事务的开始

    lr_start_transaction

    标记事务的开始

    Lr_start_transaction("trans1");

    lr_start_transaction_instance

    启动嵌套事务(由它的父事务的句柄指定)

    lr_stop_transaction

    停止事务数据的收集

    lr_stop_transaction_instance

    停止事务(由它的句柄指定)数据的收集

    lr_wasted_time

    消除所有打开事务浪费的时间

    lr_get_attrib_double

    检索脚本命令行中使用的 double 类型变量

    lr_get_attrib_long

    检索脚本命令行中使用的 long 类型变量

    lr_get_attrib_string

    检索脚本命令行中使用的字符串

    lr_user_data_point

    记录用户定义的数据示例

    lr_whoami

    将有关 Vuser 脚本的信息返回给 Vuser 脚本

    lr_get_host_name

    返回执行 Vuser 脚本的主机名

    lr_get_master_host_name

    返回运行 LoadRunner Controller 的计算机名

    lr__string

    用参数的当前值替换参数

    lr_save_string

    将以 NULL 结尾的字符串保存到参数中

    lr_save_var

    将变长字符串保存到参数中

    lr_save_datetime

    将当前日期和时间保存到参数中

    lr _advance_param

    前进到下一个可用参数

    lr _decrypt

    解密已编码的字符串

    lr__string_ext

    检索指向包含参数数据的缓冲区的指针

    lr__string_ext_free

    释放由 lr__string_ext 分配的指针

    lr_save_searched_string

    在缓冲区中搜索字符串实例,并相对于该字符串实例,将该缓冲区的一部分保存到参数中

    lr_debug_message

    将调试信息发送到输出窗口

    lr_error_message

    将错误消息发送到输出窗口

    lr_get_debug_message

    检索当前消息类

    lr_log_message

    将消息发送到日志文件

    lr_output_message

    将消息发送到输出窗口

    lr_set_debug_message

    设置调试消息类

    lr_vuser_status_message

    生成带格式的输出,并将其写到 ControllerVuser 状态区域

    lr_message

    将消息发送到 Vuser 日志和输出窗口

    lr_load_dll

    加载外部 DLL

    lr_peek_events

    指明可以暂停 Vuser 脚本执行的位置

    lr_think_time

    暂停脚本的执行,以模拟思考时间(实际用户在操作之间暂停以进行思考的时间)

    lr_continue_on_error

    指定处理错误的方法

    lr_continue_on_error (0);lr_continue_on_error (1);

    lr_rendezvous

    在 Vuser 脚本中设置集合点

    TE_wait_cursor

    等待光标出现在终端窗口的指定位置

    TE_wait_silent

    等待客户端应用程序在指定秒数内处于静默状态

    TE_wait_sync

    等待系统从 X-SYSTEM 或输入禁止模式返回

    TE_wait_text

    等待字符串出现在指定位置

    TE_wait_sync_transaction

    记录系统在最近的 X SYSTEM 模式下保持的时间

    WEB函数列表:

    web_custom_request

    允许您使用 HTTP 支持的任何方法来创建自定义 HTTP 请求

    web_image

    在定义的图像上模拟鼠标单击

    web_link

    在定义的文本链接上模拟鼠标单击

    web_submit_data

    执行“无条件”或“无上下文”的表单

    web_submit_form

    模拟表单的提交

    web_url

    加载由“URL”属性指定的 URL

    web_set_certificate

    使 Vuser 使用在 Internet Explorer 注册表中列出的特定证书

    web_set_certificate_ex

    指定证书和密钥文件的位置和格式信息

    web_set_user

    指定 Web 服务器的登录字符串和密码,用于 Web 服务器上已验证用户身份的区域

    web_cache_cleanup

    清除缓存模拟程序的内容

    web_find

    在 HTML 页内搜索指定的文本字符串

    web_global_verification

    在所有后面的 HTTP 请求中搜索文本字符串

    web_image_check

    验证指定的图像是否存在于 HTML页内

    web_reg_find

    在后面的 HTTP 请求中注册对 HTML源或原始缓冲区中文本字符串的搜索

    web_disable_keep_alive

    禁用 Keep-Alive HTTP 连接

    web_enable_keep_alive

    启用 Keep-Alive HTTP 连接

    web_set_connections_limit

    设置 Vuser 在运行脚本时可以同时打开连接的最大数目

    web_concurrent_end

    标记并发组的结束

    web_concurrent_start

    标记并发组的开始

    web_add_cookie

    添加新的 Cookie 或修改现有的 Cookie

    web_cleanup_cookies

    删除当前由 Vuser 存储的所有 Cookie

    web_remove_cookie

    删除指定的 Cookie

    web_create_html_param

    将 HTML 页上的动态信息保存到参数中。(LR 6.5 及更低版本)

    web_create_html_param_ex

    基于包含在 HTML 页内的动态信息创建参数(使用嵌入边界)(LR 6.5 及更低版本)。

    web_reg_save_param

    基于包含在 HTML 页内的动态信息创建参数(不使用嵌入边界)

    web_set_max_html_param_len

    设置已检索的动态 HTML 信息的最大长度

    web_add_filter

    设置在下载时包括或排除 URL 的条件

    web_add_auto_filter

    设置在下载时包括或排除 URL 的条件

    web_remove_auto_filter

    禁用对下载内容的筛选

    web_add_auto_header

    向所有后面的 HTTP 请求中添加自定义标头

    web_add_header

    向下一个 HTTP 请求中添加自定义标头

    web_cleanup_auto_headers

    停止向后面的 HTTP 请求中添加自定义标头

    web_remove_auto_header

    停止向后面的 HTTP 请求中添加特定的标头

    web_revert_auto_header

    停止向后面的 HTTP 请求中添加特定的标头,但是生成隐性标头

    web_save_header

    将请求和响应标头保存到变量中

    web_set_proxy

    指定将所有后面的 HTTP 请求定向到指定的代理服务器

    web_set_proxy_bypass

    指定 Vuser 直接访问(即不通过指定的代理服务器访问)的服务器列表

    web_set_proxy_bypass_local

    指定 Vuser 对于本地 (Intranet) 地址是否应该避开代理服务器

    web_set_secure_proxy

    指定将所有后面的 HTTP 请求定向到服务器

    web_set_max_retries

    设置操作步骤的最大重试次数

    web_set_timeout

    指定 Vuser 等待执行指定任务的最长时间

    web_convert_param

    将 HTML 参数转换成 URL 或纯文本

    web_get_int_property

    返回有关上一个 HTTP 请求的特定信息

    web_report_data_point

    指定数据点并将其添加到测试结果中

    web_set_option

    在非 HTML 资源的编码、重定向和下载区域中设置 Web 选项

    web_set_sockets_option

    设置套接字的选项

  • LoadRunner函数中文翻译系列之二--Check(1)

    2011-04-27 10:14:12

    web_find

     语法:
     int web_find (const char *StepName, <Attributes and Specifications list>, char *searchstring, LAST );

    参数:
     1StepName:步骤名称,在Tree视图中出现。

    2Attributes and Specifications list

    支持的属性有:

    Frame:在多Frame的情况下,定义要查找Frame的范围。

    Expect:定义在什么情况下函数检查成功:找到了指定的搜索标准或者没有找到。例如说,可以检查指定的错误信息是否出现在web页面中。合法的值有2个:foundnotfound。默认值是“found”

    Matchcase:指定搜索是否区分大小写。

    Repeat:指定当第一次发现要查找的字符串时,搜索是否继续。当一个web页面中包含多个被查找的字符串时,此参数是非常有用的。合法的值有2个:yesno。默认值是“yes”

    Report:指定在什么情况下,VuGen在执行日志中显示此函数的检查结果。合法的值有:successfailurealways。默认值是“always”

    Onfailure:此参数决定在函数检查失败后,Vuser是否中断。参数值是abort。如果指定了Onfailure=abort,当函数检查失败时,不论在运行时设置中的error-handling是什么,脚本都会中断。

    如果没有指定Onfailure=abort,那么运行时设置中error-handling将会起作用。

    支持的特性有:RightOf, LeftOf (不支持7.x及更高版本)。

    RightOf:要查找的字符串右边的内容。

    LeftOf:要查找的字符串左边的内容。

    3Searchstring:需要查找的字符串,格式为“What=stringxyz”。此搜索不区分大小写。

    4LAST:属性列表结束符。

    返回值
     整型。 成功时返回LR_PASS(0),失败时返回LR_FAIL (1)

    说明
     此函数的作用是在HTML页面中查找指定的字符串。

    此函数只能在基于HTML录制的脚本中使用。当指定的HTML请求全部完成以后,开始执行搜索过程,比web_reg_find要慢。

    web_find函数在C语言的脚本中已经被web_reg_find所替代,web_reg_find运行速度比较快,而且在HTML-basedURL-based的录制方式中都可以使用。 C语言脚本中,web_find是向后兼容的。JavaVisual Basic脚本中不再支持它。

    运行在HTTP模式下的WAP用户都和运行在WSP回放模式下的WAP用户都不支持此函数。

    web_global_verification

     语法:
     int web_global_verification (<List of Attributes>, LAST )

    参数:
     List of Attributes

    1Text:此属性是一个非空的,以NULL结尾的字符串,表示要查找的内容。语法是”Text=string”。还可以使用text flags自定义字符串。

    2TextPfx:没有指定Text的情况下使用此属性。要查找的字符串的前缀。语法是” TextPfx =string”。还可以使用text flags自定义字符串。

    3TextSfx:没有指定Text的情况下使用此属性。要查找的字符串的后缀。语法是” TextSfx =string”。还可以使用text flags自定义字符串。

    4Search:可选项,在哪里查找字符串。可选的值是:HeadersBodyNORESOURCEAll。默认值是NORESOURCE。语法是“Search=value”

    5Fail:当字符串找不到时的处理选项:Found (默认值)或NotFoundFound表示当找到对应的字符串时发生了错误(例如“Error”)。NotFound表示当找不到字符串时发生了错误。语法是“Fail=value“

    6ID:在日志文件中标识当前函数。

    LAST:属性列表结束符。

    注:text flags/IC表示忽略大小写;/BIN表示指定的是二进制数据。

    返回值
     整型。 成功时返回LR_PASS(0),失败时返回LR_FAIL (1)

    说明
     web_global_verification属于注册函数,注册一个在web页面中搜索文本字符串的请求,与web_reg_find只在下一个Action函数中执行搜索不同的是,它是在之后所有的Action类函数中执行搜索的。可以搜索页面的bodyheadershtml代码或者是整个页面。

    在检测一些应用程序级别(不通过http状态码来表现)的错误时,web_global_verification是非常有用的。如果要定位通过HTTP状态码表现的错误时,使用web_get_int_property

    查找范围:all:这个HTML页面;Headers:页面的头;body:页面的体,包含所有的资源但不包含头;NORESOURCE(默认选项):仅仅包含页面的体,把包括头和资源。

    如果不知道要查找的精确的文本,或者要查找的多个文本不是完全相同的,可以使用前缀和后缀来表示。这时需要用到TextPfxTextSfx属性。这2个属性必须同时指定,一旦指定了其中一个,就不能指定Text属性了。

    注意:web_global_verificationWAP协议下不能运行。

    web_image_check

     语法:
     int web_image_check(const char *CheckName, <List of Attributes>, <"Alt=alt"|| "Src=src">, LAST );

    参数:
     1CheckName:名称,在Tree视图中出现。

    2List of Attributes

    支持的属性有:Frame(在多Frame的情况下,定义要查找Frame的范围)。

    支持的选项有:expect, matchcase, repeat, report, onfailure

    Tip:选项跟属性的区别,大部分选项都只允许设置预定义的值,其他的值都是无效的。

    3Alt:检查图象的ALT标记。不允许空值。

    4Src:检查图象的SRC标记。不允许空值。

    5LAST:参数列表结束的指示符。

    返回值
     整型。

    说明
     web_image_check检查指定的图象是否在HTML页面中出现。

    Alt或者Src两者必须有一个在参数列表中出现。如果两项都通过,那么检查成功。

    此函数仅仅支持基于HTML的脚本。

  • LoadRunner函数中文翻译系列之三--Concurrent Group

    2011-04-27 10:12:56

    web_concurrent_start

     语法:
     int web_concurrent_start ( [char * ConcurrentGroupName,] NULL );

    参数:
     ConcurrentGroupName:可选的,并发组的标识符。

    NULL:参数列表结束的标记符。

    返回值
     整型。返回LR_PASS (0)表示成功,返回LR_FAIL (1)表示失败。

    说明
     web_concurrent_start函数是并发组开始的标记。组中所有的函数是并发执行的。并发组的结束web_concurrent_end 函数。在并发组中,可以包含的函数有:web_url、web_submit_data、web_custom_request、web_create_html_param、web_create_html_param_ex、web_reg_save_param、web_add_header。

    在并发组中的函数不是立即执行的。在并发组开始时,所有的函数首先被记录下来,当并发组结束时,所有的函数并发执行。

    所有的Web 用户,HTTP模式下的WAP用户持本函数。运行在Wireless Session Protocol(WSP)回放模式下的WAP虚拟用户,不支持本函数。

    web_concurrent_start

     语法:
     int web_concurrent_end ( reserved );

    参数:
     reserved:保留的供扩展的字段。

    返回值
     整型。返回LR_PASS (0)表示成功,返回LR_FAIL (1)表示失败。

    说明
     web_concurrent_end,并发组结束的标记。脚本执行时,碰到 web_concurrent_end函数时,开始并发执行所有记录的函数。

    在并发组中的函数不是立即执行的。在并发组开始时,所有的函数首先被记录下来,当并发组结束时,所有的函数并发执行。

    可以并发执行的函数的个数是有限制的,使用运行时设置-Netword标签页的Concurrent Connection来设置。

  • LoadRunner之协议选择

    2011-04-27 10:09:10

    在学习LoadRunner协议选择之前,我觉得我们有必要了解一下协议的基本概念。首先我们知道,计算机与计算机之间的通信都离不开通信协议,接着我们来说说通信协议的概念。通信协议是什么,通信协议实际上是一组规定和约定的集合。说白了就是两台或者多台计算机在通信时必须约定好本次通信做什么,例如是进行文件传输,还是发送电子邮件;然后约定怎样通信,什么时间通信等。因此,通信双方要遵从相互可以接受的协议(相同或兼容的协议)才能进行通信,如目前因特网上广泛使用的TCP/IP协议等,任何计算机连入网络后只要运行TCP/IP协议,就可访问因特网。

    了解了协议的基本概念和作用之后,我们来说说LoadRunner的协议选择。LoadRunner首先是一个测试工具,其次是一个性能测试工具,然后是该工具是一个基于协议,也就是说LoadRunner测试的对象都需要使用通信协议,对于那些不使用通信协议仅仅进行本地处理的软件例如Microsoft Word,LoadRunner就不适用。

    说到通信协议我们来熟悉一下协议的分层,按照OSI的分层模型,分层结构如下:

    OSI七层模型
    TCP/IP协议

    应用层
    应用层(Application)

    表示层

    会话层

    传输层
    传输层(Transport)

    网络层
    网络层(Internet)

    数据链路层
    网络接口层(Network)

    物理层

    按照TCP/IP协议的分层,分层结构如下:

    TCP/IP协议
    对应的服务

    应用层
    WWW、SMTP、FTP、Telnet、Gopher、SNMP

    Socket、NetBIOS

    传输层
    TCP、UDP

    网络层
    IP(ARP、RARP、ICMP)

    网络接口层
    Ethernet、X.25、PPP、SLIP

    第一个分层是由OSI制定但不实用,后一个是目前广泛使用且被业界认做既定标准的协议分层,下文探讨的LoadRunner协议选择即按TCP/IP协议的分层模型讨论。

    接着来说说LoadRunnerVuGen中的协议分类,VuGen(LR8.1)中的协议分类如下表所示:

    应用程序部署解决方案
    Citrix ICA

    客户端/ 服务器
    DB2 CLI、DNS、Informix、MS SQL Server、ODBC、Oracle(2 层)、Sybase Ctlib、Sybase Dblib 和 Windows Sockets 协议

    自定义
    C 模板、Visual Basic 模板、Java 模板、Javascript. 和 VBScript. 类型的脚本。

    分布式组件
    适用于 COM/DCOM、Corba-Java 和 Rmi-Java 协议。

    电子商务
    FTP、LDAP、Palm、PeopleSoft 8 mulit-lingual、SOAP、Web(HTTP/HTML) 和双 Web/WinSocket 协议。

    Enterprise Java Bean
    EJB 测试和 Rmi-Java 协议。

    ERP/CRM
    Baan、Oracle NCA、Peoplesoft-Tuxedo、SAP-Web、SAPGUI、Siebel-DB2 CLI、Siebel-MSSQL、Siebel-Web 和 Siebel-Oracle 协议

    传统
    终端仿真 (RTE)。

    邮件服务
    Internet 邮件访问协议 (IMAP)、MS Exchange (MAPI)、POP3 和SMTP。

    中间件
    Jacada 和 Tuxedo (6、7)协议。

    流数据
    Media Player (MMS) 和 Real 协议。

    无线
    i-Mode、VoiceXML 和 WAP 协议。

    仔细研究发现LoadRunner VuGen中的协议与文章开头所说的通信协议还是有一定的区别的,例如像LoadRunner VuGen中的C 模板、Visual Basic 模板、Java 模板、Javascript. 和 VBScript. 类型的脚本均为开发语言,非通信协议,但LoadRunner即把它列在这儿,我们也就暂且认可。

          了解了LoadRunner的协议类型之后,我们进入正式话题,即测试时如何选择协议。

          正式测试之前,测试人员都需要预先熟悉被测对象,我们需要知道我们的被测对象是一个什么样的结构,是B/S结构还是C/S结构,了解这个之后,我们还需要了解被测对象所使用的协议是什么,也许有的人说我们的程序使用的是TCP/IP协议,其实他的回答跟我们需要知道的差别很大,因为我们知道,所有的通信软件都需要使用TCP/IP协议,为什么呢,因为这个协议是底层协议,所有应用层数据都必须经过这个协议封装之后才能向更底一层传输。我们需要知道的是被测对象在应用层使用的是什么协议,就像我们使用邮件客户端发送邮件一样,我们知道使用的应用层协议是SMTP,使用邮件客户端接收邮件时使用的时POP3协议。了解上述信息之后,我们的定位也就准确了,也就是说在LoadRunner中所说的协议基本上都是应用层协议(也有底层协议,例如 Windows Sockets),知道这点之后,我们在询问开发人员时就避免了不少麻烦,省的开发人员告诉你我们使用的是TCP/IP协议或者其他底层协议。

          了解了LoadRunner中的协议之后,我们就进入协议确定阶段,协议的确定,通常有如下几种方法:

    1、通过询问开发人员获知所使用的协议,通常这是最简单也是最直接的方法;因为没有人比开发人员更清楚他们所开发的应用程序使用的什么通信协议了;

    2、通过概要或详细设计手册获知所使用的协议,在没有开发人员支持的情况,通过概要设计或详细设计获知所使用的协议不失为第二简便方法;

    3、通过协议分析工具捕包分析,然后确定被测对象所使用的协议。在使用协议分析工具分析协议过程当中一定要摒除底层协议,不要被底层协议所迷惑;

    4、通过以往测试经验确定被测对象所使用的协议,当然通过这种方法确定的协议有一定的不准确性;

    通过以上四种方法我们基本就确定了录制时应该选择什么协议,光确定协议是没有用的,最主要的是付诸行动,确定了协议之后,我们进入VuGen开始录制脚本,录制完成后看看是否生成相应脚本,如果脚本内容为空可能我们选择的协议不正确,我们可以尝试选择其他协议。

    一般来说协议选择有如下原则:

    B/S结构,选择WEB(Http/Html)协议;

    C/S结构,可以根据后端数据库的类型来选择,如SybaseCTLib协议用于测试后台的数据库为Sybase的应用;MS SQL Server协议用与测试后台数据库为SQL Server的应用;对于一些没有数据库的Windows应用,可选用Windows Sockets底层协议;使用了数据库但使用的是ODBC连接的数据则选择ODBC协议;

    对于有些使用纯JAVA编写的C/S结构的东东,采用JAVA,而且不能录制只能手工编写代码(工作量和难度还是有的)。同样不能录制的还包括C、VB Script、VB、VBNet User协议。

    对于Windows Sockets协议来说,最适合的那些基于Socket开发的应用程序;但是由于网络通讯的底层都是基于Socket的,因此几乎所有的应用程序都能够通过Socket来录制,哪可能有人会问,哪既然Socket都能录制下来,还要那么多协议做什么,价格还贼贵,其实最主要的原因就是Socket录制的代码可读性较差,如果Socket的脚本可读性较高的话,实话就没有其他协议出现的必要性了。

    对于邮件来说,首先要看你收邮件的途径,如果你通过WEB页面收发邮件,毫无疑问,你选择协议时就需要选择HTTP协议,如果你通过邮件客户端,像OutLook、FoxMail之类的,则需要根据操作不同选择不同的协议了,例如发邮件你可能要选择SMTP、收邮件你可能需要选择POP3。

  • LR中常用的C函数

    2011-04-27 10:07:18

    LR中常用的C函数

    char *strcat ( char *to, const char *from );

    功能:链接两个字符串。

    例子:

    这个例子是用strcat链接字符串:zee和slo@hotmail.co

    脚本如下:

        char test[1024], *a = "slo@hotmail.com";

        strcpy(test, "zee");

        strcat(test, a);

        lr_output_message("We can see %s",test);


    运行后在executon log中看到如下语句:

    Starting action Action.
    Action.c(16): We can see zeeslo@hotmail.com

    2    strchr
    char *strchr ( const char *string, int c );

    功能:返回字符串中指定字符后面的字符串。

    例子:

    这个例子是返回第一个出现e字符以后所有的字符,和最后一次出现e字符以后所有的字符。

    脚本如下:
        char *string = "Zee is a tester";
        char *first_e, *last_e;

        first_e = (char *)strchr(string, 'e');
        lr_output_message("We can see the first occurrence of e: %s",first_e);
        last_e = (char *)strrchr(string, 'e');
        lr_output_message("We can see the last occurrence of e: %s", last_e);

    运行后在executon log中看到如下语句:
    Starting action Action.
    Action.c(12): We can see the first occurrence of e: ee is a tester
    Action.c(14): We can see the last occurrence of e: er
    3    Strcmp&stricmp
    int strcmp ( constchar *string1, const char *string2 );大小写敏感。
    int stricmp ( const char *string1, const char *string2 );大小写不敏感。

    功能:比较字符串。

    例子:

    按是否区分大小写对比两个字符串,并打印出它们的大小关系。

    脚本如下:
        int result;
        char tmp[20];
        char string1[] = "We can see the string:ZEE";
        char string2[] = "We can see the string:zee";

        result = strcmp( string1, string2 ); /*区分大小写,比较字符串 */

        if( result > 0 )
          strcpy( tmp, "大于" );
        else if( result < 0 )
          strcpy( tmp, "小于" );
        else
          strcpy( tmp, "等于" );

        lr_output_message( "strcmp: String 1 %s string 2", tmp );

        result = stricmp( string1, string2 ); /* 不区分大小写,比较字符串 */

        if( result > 0 )
          strcpy( tmp, "大于" );
        else if( result < 0 )
          strcpy( tmp, "小于" );
        else
          strcpy( tmp, "等于" );

        lr_output_message( "stricmp: String 1 %s string 2", tmp );    

    运行后在executon log中看到如下语句:
    Starting action Action.
    Action.c(22): strcmp: String 1 小于 string 2
    Action.c(33): stricmp: String 1 等于 string 2

    4    strcpy
    char *strcpy ( char *dest, const char *source );

    功能:复制一个字符串到另一个字符串中。

    例子:

    复制一个字符串到字符数组中,并打印出来。

    脚本如下:

        char test[1024];

        strcpy(test, "what can we see?    ");

        lr_output_message("%s", test);

    运行后在executon log中看到如下语句:
    Starting action Action.
    Action.c(10): what can we see?   
    5    Strdup& strlwr
    char *strdup ( const char *string );

    复制一个字符串。

    char *strlwr ( char *string );

    转换成小写字母。

    例子:

    在这个例子中,Vuser的组名被转换为小写字母。但是lr_whoami把组名作为静态buffer返回。这样的buffer不能被操作。如果有操作需要,就复制这个静态buffer。

    脚本如下:

        int id;
        char *groupname_static, *groupname;

        /* 从VuGen中得到组名 */
        lr_whoami(&id, &groupname_static, NULL);
        lr_output_message("groupname=%s", groupname_static);

        /*复制这个静态组名以便我们可以操作它 */
        groupname = (char *)strdup(groupname_static);
        groupname = (char *)strlwr(groupname);
        lr_output_message("lower case groupname=%s", groupname);

        free(groupname);
    上述脚本用vugen保存为:CHANGE

    在controller中运行(设置为总是发送消息)
    运行后在log中看到如下语句:
    Starting action Action. [MsgId: MMSG-15919]
    Action.c(11): groupname=CHANGE     [MsgId: MMSG-17999]
    Action.c(16): lower case groupname=change    [MsgId: MMSG-17999]
    6    Strlen
    size_t strlen ( constchar *string );

    功能:返回字符串长度(bytes).

    例子:

    这个例子很简单,就是得到一个字符串中的字符的个数。然后打印出来。

    脚本如下:

        char *str = "Zee is a tester";
        unsigned int len;

        len = strlen(str);

        lr_output_message("The sentence has %d letters",len);

    运行后在log中看到如下语句:
    Action.c(13): The sentence has 15 letters
    7    Strncat
    char *strncat ( char *to_string, const char *from_string, size_t n );

    把一个字符串连接到另一个字符串后面。

    例子:

    在这里,我随便写了两个字符串,用此函数把他们连接起来,并打印出来。

    脚本如下:


    char str1[]="Zee is ";
    char str2[]="a tester.";
    lr_output_message("What can we see?");
    lr_output_message("The str1 is %s.",str1);

    strncat(str1,str2,20);
    lr_output_message("The str1 is %s.",str1);


    运行后在log中看到如下语句:
    Action.c(9): What can we see?
    Action.c(10): The str1 is Zee is .
    Action.c(13): The str1 is Zee is a tester..
    注:我们可以看到,没有连接前的str1是:Zee is,连接后的字符串是:Zee is a tester。也可以看看strcat函数。
    8    strncmp
    int strncmp ( constchar *string1, const char *string2, size_t n );

    对比两个字符串的前n位。

    例子:

    对比两个字符串,并把对比结果打印出来。这里我和上面的strcmp一起写。

    脚本如下:

    char result;
    char str1[]="Zee is a tester.";
    char str2[]="Zee is a tester.";
    char str3[]="zee is a tester?";

    result = strcmp(str1,str2);

    if(result > 0)
          lr_output_message("str1 is greater than str2.");
    else if(result < 0)
          lr_output_message("str1 is less than str2.");
    else
          lr_output_message("str1 is equal to str2.");

    result = strncmp( str1, str3 , 30);


    if(result > 0)
          lr_output_message("str1 is greater than str3.");
    else if(result < 0)
          lr_output_message("str1 is less than str3.");
    else
          lr_output_message("str1 is equal to str3.");

    运行后在log中看到如下语句:
    Starting iteration 1.
    Starting action Action.
    Action.c(18): str1 is equal to str2.
    Action.c(28): str1 is less than str3.
  • LR字符串与参数的操作及转换技巧

    2011-04-27 10:05:07

    LR字符串与参数的操作及转换技巧

         刚开始学LR时,经常搞不清楚变量和参数的区别与用法,最近在一次脚本编写中,整理出来的一些小技巧,与大家一起分享。

      //字符串复制
            strcpy(str,"Hello ") ;

            //字符串连接
            strcat(str,"World !");
            lr_message("str: %s",str);


            //变量转为参数,将变量str的值存到参数Param中
            lr_save_string(str,"Param");


            //参数复制
            lr_save_string(lr_eval_string("{Param}"),"Param_1");

            //参数转为变量
            strcpy(str1,lr_eval_string("{Param_1}"));
            lr_message("str1: %s",str1);


            //参数名称格式化输出到变量中
            sprintf(str2,"{Param_%d}",1);
            lr_message("str2: %s",lr_eval_string(str2));


            运行结果:
            str: Hello World !
            vuser_init.c(14): Notify: Saving Parameter "Param = Hello World !"
            vuser_init.c(19): Notify: Parameter Substitution: parameter "Param" = "Hello World !"
            vuser_init.c(19): Notify: Saving Parameter "Param_1 = Hello World !"
            vuser_init.c(24): Notify: Parameter Substitution: parameter "Param_1" = "Hello World !"
            str1: Hello World !
            vuser_init.c(30): Notify: Parameter Substitution: parameter "Param_1" = "Hello World !"
            str2: Hello World !

Open Toolbar