qileilove

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

自动化单元测试实践之路

  自动化单元测试并不是什么新鲜事物,它应该是团队持之以恒的事情,可能有很多团队知道如何去做,但是还做得不够好;还有不少团队不知道如何去做,甚至有一些旧系统还不敢去重构,还在坚持着Java中的main方法调用的方式来执行,在漫长等待构建结果。
  本文主要讲基于Java项目如何做自动化单元测试的实践。
  1 是否值得
  关于单元测试的意义,详细参考stackoverflow这篇文章
  http://stackoverflow.com/questions/67299/is-unit-testing-worth-the-effort,
  Martin Fowler在博客(http://martinfowler.com/bliki/TestPyramid.html)中解释了
  TestPyramid,如下图所示:
  
  Unit是整个金字塔的基石(在建筑行业,基石是做建筑物基础的石头),如果基石不稳,Service和UI何谈有构建意义呢?只有基石稳如磐石,上层建筑才够坚固。
  本来想拿瑞士做钟表的例子来说明下,但同事说的汽车例子更好。一辆汽车由许多配件组成,如果有以下两种选择,你会选择哪个呢?
  所有单元配件没有测试过,在4S店,销售人员告诉你:刚组装好,已经开了一天,能跑起来,你可以试试;
  所有单元配件在生产过程已经经过严格测试,在4S点,销售人员告诉你,已经通过国家认证,出厂合格,有质量保证,你可以试试;
  答案不言而喻了。
  实施单元测试,并不代表你的生产效率能提高迅猛,反而有时候阻碍了瞬间的生产效率(传统的开发一个功能,看似就算完成的动作,增加单元测试看起来无法是浪费时间),但是,它最直接的是提升产品质量,从而提升市场的形象,间接才会提升生产效率。
  做产品,到底是要数量,还是质量呢?这个应该留给老板们去回答,看企业是否需要长远立足。
  2 关键部分
  自动化单元测试有四个关键组成部分要做到统一,如图所示:
 
 图-2-1-关键组成部分
  配置管理:使用版本控制
  版本控制系统(源代码控制管理系统)是保存文件多个版本的一种机制。一般来说,包括Subversion、Git在内的开源工具就可以满足绝大多数团队的需求。所有的版本控制系统都需要解决这样一个基础问题: 怎样让系统允许用户共享信息,而不会让他们因意外而互相干扰?
  如果没有版本控制工具的协助,在开发中我们经常会遇到下面的一些问题:
  一、 代码管理混乱。
  二、 解决代码冲突困难。
  三、 在代码整合期间引入深层BUG。
  四、 无法对代码的拥有者进行权限控制。
  五、 项目不同版本发布困难。
  对所有内容都进行版本控制
  版本控制不仅仅针对源代码,每个与所开发的软件相关的产物都应该被置于版本控制下,应当包括:源代码、测试代码、数据库脚本、构建和部署脚本、文档、web容器(tomcat的配置)所用的配置文件等。

保证频繁提交可靠代码到主干
  频繁提交可靠、有质量保证的代码(编译通过是最基本要求),能够轻松回滚到最近可靠的版本,代码提交之后能够触发持续集成构建,及时得到反馈。
  提交有意义的注释
  强制要求团队成员使用有意义注释,甚至可以关联相关开发任务的原因是:当构建失败后,你知道是谁破坏了构建,找到可能的原因及定位缺陷位置。这些附加信息,可以缩短我们修复缺陷的时间。示例:团队使用了svn和redmine,注释是:
  refs #任务id 提交说明
  每个任务下可以看到多次提交记录:
  
图-2-2-相关修订版本
  所有的代码文件编码格式统一使用UTF-8
  上班前更新代码,下班前提交代码
  前一天,团队其他成员可能提交了许多代码到svn,开始新的一天工作是,务必更新到最新版本,及时发现问题(例如代码冲突)并解决;
  当日事,当日毕,下班别把当天的编码成果仅保存在本地,应当提交到svn,次日团队更新就可以获取到最新版本,形成良性循环。
  构建管理:使用Maven构建工具
  Maven是基于项目对象模型(POM),通过为Java项目的代码组织结构定义描述信息来管理项目的构建、报告和文档的软件项目管理工具。使用“惯例胜于配置”(convention over configuration)的原则,只要项目按照Maven制定的方式进行组织,它就几乎能用一条命令执行所有的构建、部署、测试等任务,却不用写很多行的XML(消除Ant文件中大量的样板文件)。
  或许,使用Ant来构建的团队要问,为什么用Maven呢?简单来说两点
  1、对第三方依赖库进行统一的版本管理
  说实话,ant处理依赖包之间的冲突问题,还是得靠人工解决,这个对于研发来说是消耗时间的,倒不如把节省的时间投入到业务中去。另外再也不用每个项目繁琐复制spring.jar了,通过maven自动管理Java库和项目间的依赖,打包的时候会将所有jar复制到WEB- INF/lib/目录下。
  2、统一项目的目录结构。
 保证所有项目的目录结构在任何服务器上都是一样的,每个目录起什么作用都很清楚明了。
  3、统一软件构建阶段
  http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
  Maven2把软件开发的过程划分成了几个经典阶段,比如你先要生成一些java代码,再把这些代码复制到特定位置,然后编译代码,复制需要放到classpath下的资源,再进行单元测试,单元测试都通过了才能进行打包,发布。
  测试框架:JUnit&Mockito
  JUnit
  JUnit是一个Java语言的单元测试框架。
  2013年见过一个旧项目,测试代码还是以main作为入口,为什么要使用JUnit?
  JUnit 的优点是整个测试过程无人值守,开发无须在线参与和判断最终结果是否正确,可以很容易地一次性运行多个测试,使得开发更加关注测试逻辑的编写,而不是增加构建维护时间。
  团队示例代码:
// 功能代码
package com.chinacache.portal.service;
public class ReportService {
public boolean validateParams() {
}
public String sendReport(Long id) {
}
public String sendReport(Long id, Date time) {
}
}
// 单元测试代码
package com.chinacache.portal.service; // 必须与功能代码使用相同 package
public class ReportServiceUnitTest { // 测试类名以 UnitTest (单元测试) 或 InteTest (集成测试) 结尾
// 测试方法名以 test 开头,然后接对应的功能方法名称
@Test
public void testValidateParams() {
}
// 如果功能方法存在重载,则再接上参数类型
@Test
public void testSendReportLong() {
}
// 如果一个功能方法对应多个测试方法,不同测试方法可使用简洁而又有含义的单词结尾,例如 success、fail 等
@Test
public void testSendReportLongDateSuccess() {
}
// 这样通过测试方法名即可知道:测的是哪个功能方法,哪种情况
@Test
public void testSendReportLongDateFail() {
}
}
  Mockito
  Mockito是一个针对Java的mocking框架。使用它可以写出干净漂亮的测试用例和简单的API。它与EasyMock和jMock很相似,通过在执行后校验什么已经被调用,消除了对期望行为(expectations)的需要,改变其他mocking库以“记录-回放”(这会导致代码丑陋)的测试流程,使得自身的语法更像自然语言。
  Mockito示例:
List mock = mock(List.class);
when(mock.get(0)).thenReturn("one");
when(mock.get(1)).thenReturn("two");
someCodeThatInteractsWithMock();
verify(mock).clear();
EasyMock示例:
List mock = createNiceMock(List.class);
expect(mock.get(0)).andStubReturn("one");
expect(mock.get(1)).andStubReturn("two");
mock.clear();
replay(mock);
someCodeThatInteractsWithMock();
verify(mock);
  官方对比文章:http://code.google.com/p/mockito/wiki/MockitoVSEasyMock
  反馈平台:Jenkins&Sonar
  持续集成平台:Jenkins
  Jenkins 的前身是 Hudson 是一个可扩展的持续集成引擎,主要用于:
  持续、自动地构建测试软件项目
  监控一些定时执行的任务
  Jenkins将作为自动化单元测试持续集成的平台,实现自动化构建。
图-2-3-Jenkins平台
  代码质量管理平台:Sonar
  Sonar (SonarQube)是一个开源平台,用于管理源代码的质量。Sonar 不只是一个质量数据报告工具,更是代码质量管理平台。支持的语言包括:Java、PHP、C#、C、Cobol、PL/SQL、Flex 等。
  主要特点:
  代码覆盖:通过单元测试,将会显示哪行代码被选中
  改善编码规则
  搜寻编码规则:按照名字,插件,激活级别和类别进行查询
  项目搜寻:按照项目的名字进行查询
  对比数据:比较同一张表中的任何测量的趋势
  Sonar将作为自动化单元测试反馈报告统一展现平台,包括:
  单元测试覆盖率、成功率、代码注释、代码复杂度等度量数据的展现。
  
图-2-4 Sonar平台
  3 原则
  自动化测试金字塔,也称为自动化分层测试,Unit是整个金字塔的基石,最重要特点是运行速度非常快;第二个重要特点是UT应覆盖代码库的大部分,能够确定一旦UT通过后,应用程序就能正常工作。
  Unit:70%,大部分自动化实现,用于验证一个单独函数或独立功能模块的代码;
  Service:20%,涉及两个或两个以上,甚至更多模块之间交互的集成测试;
  UI:10%,覆盖三个或以上的功能模块,真实用户场景和数据的验收测试;
  这里仅仅列举了每个层次的百分比,实际要根据团队的方向来做调整。
  自动化单元测试原则
  提交代码、运行测试的重点是什么?快速捕获那些因修改向系统中引入的最常见错误,并通知开发人员,以便他们能快速修复他们。提交阶段提供反馈的价值在于,对它的投入可以让系统高效且更快地工作。
  隔离UI操作
  UI应当作为更高层次的测试Level,需要花费大量时间准备数据,业务逻辑复杂,过早进入UI阶段,容易分散开发的单元测试精力。
  隔离数据库以及文件读写网络开销等操作
  自动化测试中如果需要将结果写入数据库,然后再验证改结果是否被正确写入,这种验证方法简单、容易理解,但是它不是一个高效的方法。这个应当从集成测试的Level去解决。
  首先:与数据库的交互,是漫长的,甚至有可能要投入维护数据库的时间,那将成为快速测试的一个障碍,开发人员不能得到及时有效的反馈。假设,我需要花费一个小时,才能验证完毕与数据库交互的结果,这种等待是多么漫长呀。
  其次,数据管理需要成本,从数据的筛选(线上数据可能是T级)到测试环境的M级别,如何把筛选合适的大小,这都使得管理成本增加(当然在集成测试中可以使用DBUnit来解决部分问题)。
  最后,如果一定要有读写操作才能完成的测试,也要反思代码的可测试性做的如何?是否需要重构。
  单元测试决不要依赖于数据库以及文件系统、网络开销等一切外部依赖。
  使用Mock替身与Spring容器隔离
  如果在单元测试中,还需要启动Spring容器进行依赖注入、加载依赖的WebService等,这个过程是相当消耗时间的。
  可以使用模拟工具集:Mockito、EasyMock、JMock等来解决,研发团队主要是基于Mockito的实践。与需要组装所有的依赖和状态相比,使用模拟技术的测试运行起来通常是非常快,这样子开发人员在提交代码之后,可以在持续集成平台快速得到反馈。
  设计简单的测试
  明确定义方法:
  成功:public void testSendReportLongDateSuccess()
  失败:public void testSendReportLongDateFail(),可以包括异常
  和单一的断言,避免在一个方法内使用多个复杂断言,这会造成代码结构的复杂,使得测试的复杂性提高。
  定义测试套件的运行时间
  使用Mock构建的单元测试,每个方法的构建时间应该是毫秒级别,整个类是秒级别,理想的是整体构建时间控制在5分钟以内,如果超过怎么办呢?
  首先,拆分成多个套件,在多台机器上并行执行这些套件;
  其次,重构那些运行时间比较长且不经常失败的测试类;
  更多参考推荐阅读:《Unit Testing Guidelines》
  http://geosoft.no/development/unittesting.html
  4 流程
  
图-4-1-典型工作流程
  开发人员遵循每日构建原则,提交功能代码、测试代码(以UnitTest结尾的测试类)到Svn;
  Jenkins平台,根据配置原则(假设配置定时器每6分钟检查Svn有代码更新则构建)进行:代码更新、代码编译、UnitTest、持续反馈的流水线工作;
  构建结果发送到Sonar,并且把失败的构建以邮件方式通知影响代码的开发人员;
  开发人员、测试人员需要在Sonar平台进行review;
  5 实践
  Jenkins配置重点
  构建触发器:推荐使用PollSCM
  Poll SCM:定时检查源码变更(根据SCM软件的版本号),如果有更新就执行checkout。
  Build periodically:周期进行项目构建(它不care源码是否发生变化)。
  配置时间:H/6 * * * *
  Build配置
  Goals and options:emma:emma -Dtest=*UnitTest soanr:sonar
  注明:
  emma:emma,Add the "emma:emma" goal to your build to generate Emma reports;
  -Dtest=*UnitTest,参数配置,运行以UnitTest结尾的测试类;
  sonar:sonar,来触发静态代码分析。
  需要安装Emma Plugin(https://wiki.jenkins-ci.org/display/JENKINS/Emma+Plugin)
  构建后操作
  增加Aggregate downstream test results,勾选自动整合所有的downstream测试;
  增加Editable Email Notification,在“高级”选项增加触发器“Unstable”,
  勾选“Send To Committers”,Check this checkbox to send the email to anyone who checked in code for the last build。
  注明:Editable Email Notification插件是 https://wiki.jenkins-ci.org/display/JENKINS/Email-ext+plugin
  另外一些Jenkins的单元测试覆盖率展现方式,可以查看官网。
  构建管理工具(Maven)
  项目统一使用Maven进行构建管理,在pom.xml中进行依赖jar包配置
  持续集成服务器上同时需要安装Maven,setting.xml除了配置仓库之外,还需要配置sonar,包括sonar服务器地址、数据库连接方式:
<profile>
<id>sonar</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<!-- EXAMPLE FOR MYSQL -->
<sonar.jdbc.url>
jdbc:mysql://127.0.0.1:3306/sonar?useUnicode=true&characterEncoding=utf8
</sonar.jdbc.url>
<sonar.jdbc.driverClassName>com.mysql.jdbc.Driver</sonar.jdbc.driverClassName>
<sonar.jdbc.username>sonar</sonar.jdbc.username>
<sonar.jdbc.password>sonar</sonar.jdbc.password>
<!-- SERVER ON A REMOTE HOST -->
<sonar.host.url>http:/127.0.0.1:9000</sonar.host.url>
</properties>
</profile>
  Mockito配置重点
  所有单元测试继承MockitoTestContext父类
  MockitoTestContext 父类:
package com.chinacache.portal;
import java.util.Locale;
import org.junit.BeforeClass;
import org.mockito.MockitoAnnotations;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.chinacache.portal.web.util.SessionUtil;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
/**
* Mockito 测试环境。继承该类后,Mockito 的相关注解 (@Mock, @InjectMocks, ...) 就能生效
*/
public class MockitoTestContext {
public MockitoTestContext() {
MockitoAnnotations.initMocks(this);
}
}
BillingBusinessManager 源码:
package com.chinacache.portal.service.billing;
//引入包忽略...
/**
* 计费业务相关的业务方法
*/
@Transactional
public class BillingBusinessManager {
private static final Log log = LogFactory.getLog(BillingBusinessManager.class);
@Autowired
private UserDAO userDAO;
@Autowired
private BillingBusinessDAO billingBusinessDAO;
@Autowired
private BillingBusinessSubscriptionDAO billingBusinessSubscriptionDAO;
@Autowired
private BillingBusinessSubscriptionDetailDAO billingBusinessSubscriptionDetailDAO;
@Autowired
private BillingRegionSubscriptionDAO billingRegionSubscriptionDAO;
@Autowired
private BillingRegionDAO billingRegionDAO;
@Autowired
private ContractTimeManager contractTimeManager;
/**
* 根据id查询业务信息
* @return 如果参数为空或者查询不到数据,返回空列表
* O 中的中、英文业务名来自 BILLING_BUSINESS 表
*/
public List getBusinessesByIds(List businessIds) {         return billingBusinessDAO.getBusinessbyIds(businessIds); } }
BillingBusinessManagerUnitTest类:
//引入包忽略...
public class BillingBusinessManagerUnitTest extends MockitoTestContext {
@InjectMocks
private BillingBusinessManager sv;
@Mock
private BillingBusinessDAO billingBusinessDAO;
@Test
public void testGetBusinessesByIds() {
List<BusinessVO> expected = ListUtil.toList(new BusinessVO(1l, "a", "b"));
//简洁的语法如下所示
when(billingBusinessDAO.getBusinessbyIds(anyListOf(Long.class))).thenReturn(expected);
List<Long> businessIds = ListUtil.toList(TestConstants.BUSINESS_ID_HTTP_WEB_CACHE);
List<BusinessVO> actual = sv.getBusinessesByIds(businessIds);
Assert.assertEquals(expected, actual);
}
}
  更多Mockito的使用,可以参考官网:http://code.google.com/p/mockito/
  6 总结
  如何加强开发过程中的自测环节,一直都是个头痛的问题,开发的代码质量究竟如何?模块之间的质量究竟如何?回归测试的效率如何?重构之后,如何快速验证模块的有效性?
  这些在没有做自动化单元测试之前,都是难以考究的问题。唯有通过数据去衡量,横向对比多个版本的构建分析结果,才能够发现整个项目质量的趋势,是提升了,还是下降了,这样开发、测试人员才能够有信心做出恰当的判断。
  当然,单元测试也不是银弹,即便项目的覆盖率达到100%,也不能表明产品质量没有任何问题,不会产生任何缺陷。重点在于确保单元测试环节的实施,可以提前释放压力、风险、暴露问题等多个方面,改变以往没有单元测试,所有问题都集中到最后爆发的弊端。
  最后,用一张图来做个对比:
  
图-6-1-使用前后对比
  增加单元测试之后:
  开发效率有望提升5-20%;重构、回归测试效率提升10%,降低出错的几率,总体代
  码质量提升;
  在开发过程中暴露更多问题,将风险和压力提前释放,持续构建促使开发重视代码质量;
  UnitTest质量对于团队来说,是可视化了,交付的是有质量的产品,而不是数量;

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

使用异步Servlet改进应用性能

Nikita Salnikov Tarnovski是plumbr的高级开发者,也是一位应用性能调优的专家,他拥有多年的性能调优经验。近日,Tarnovski撰文谈到了如何通过异步Servlet来改进常见的Java Web应用的性能问题。
  众所周知,Servlet 3.0标准已经发布了很长一段时间,相较于之前的2.5版的标准,新标准增加了很多特性,比如说以注解形式配置Servlet、web.xml片段、异步处理支持、文件上传支持等。虽然说现在的很多Java Web项目并不会直接使用Servlet进行开发,而是通过如Spring MVC、Struts2等框架来实现,不过这些Java Web框架本质上还是基于传统的JSP与Servlet进行设计的,因此Servlet依然是最基础、最重要的标准和组件。在Servlet 3.0标准新增的诸多特性中,异步处理支持是令开发者最为关注的一个特性,本文就将详细对比传统的Servlet与异步Servlet在开发上、使用上、以及最终实现上的差别,分析异步Servlet为何会提升Java Web应用的性能。
  本文主要介绍的是能够解决现代Web应用常见性能问题的一种性能优化技术。当今的应用已经不仅仅是被动地等待浏览器来发起请求,而是由应用自身发起通信。典型的示例有聊天应用、拍卖系统等等,实际情况是大多数时间与浏览器的连接都是空闲的,等待着某个事件来触发。
  这种类型的应用自身存在着一个问题,特别是在高负载的情况下问题会变得更为严重。典型的症状有线程饥饿、影响用户交互等等。根据近一段时间的经验,我认为可以通过一种相对比较简单的方案来解决这个问题。在Servlet API 3.0实现成为主流后,解决方案就变得更加简单、标准化且优雅了。
  在开始介绍解决方案前,我们应该更深入地理解问题的细节。还有什么比看源代码更直接的呢,下面就来看看下面这段代码:
@WebServlet(urlPatterns = "/BlockingServlet")
public class BlockingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
try {
long start = System.currentTimeMillis();
Thread.sleep(2000);
String name = Thread.currentThread().getName();
long duration = System.currentTimeMillis() - start;
response.getWriter().printf("Thread %s completed the task in %d ms.", name, duration);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
  上面这个Servlet主要完成以下事情:
  请求到达,表示开始监控某些事件。
  线程被阻塞,直到事件发生为止。
  在接收到事件后,编辑响应然后将其发回给客户端。
  为了简化,代码中将等待部分替换为一个Thread.sleep()调用。
  现在,你可能会觉得这就是一个挺不错的Servlet。在很多情况下,你的理解都是正确的,上述代码并没有什么问题,不过当应用的负载变大后就不是这么回事了。
  为了模拟负载,我通过JMeter创建了一个简单的测试,我会启动2,000个线程,每个线程运行10次,每次都会向/BlockedServlet这个地址发出请求。将这个Servlet部署在Tomcat 7.0.42中然后运行测试,得到如下结果:
  平均响应时间:19,324ms
  最快响应时间:2,000ms
  最慢响应时间:21,869ms
  吞吐量:97个请求/秒
  默认的Tomcat配置有200个工作线程,此外再加上模拟的工作由2,000ms的睡眠时间来表示,这就能比较好地解释最快与最慢的响应时间了,每个线程都会睡眠2秒钟。再加上上下文切换的代价,因此97个请求/秒的吞吐量基本上是符合我们的预期的。
  对于绝大多数的应用来说,这个吞吐量还算是可以接受的。重点来看看最慢的响应时间与平均响应时间,问题就变得有些严重了。经过20秒而不是期待的2秒才能得到响应显然会让用户感到非常不爽。 下面我们来看看另外一种实现,利用Servlet API 3.0的异步支持:
@WebServlet(asyncSupported = true, value = "/AsyncServlet")
public class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Work.add(request.startAsync());
}
}
public class Work implements ServletContextListener {
private static final BlockingQueue queue = new LinkedBlockingQueue();
private volatile Thread thread;
public static void add(AsyncContext c) {
queue.add(c);
}
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(2000);
AsyncContext context;
while ((context = queue.poll()) != null) {
try {
ServletResponse response = context.getResponse();
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.printf("Thread %s completed the task", Thread.currentThread().getName());
out.flush();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
context.complete();
}
}
} catch (InterruptedException e) {
return;
}
}
}
});
thread.start();
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
thread.interrupt();
}
}
  上面的代码看起来有点复杂,因此在开始分析这个解决方案的细节信息之前,我先来概述一下这个方案:速度上提升了75倍,吞吐量提升了20倍。看到这个结果,你肯定迫不及待地想知道这个示例是如何做到的吧。
  这个Servlet本身是非常简单的。需要注意两点,首先是声明Servlet支持异步方法调用:
  @WebServlet(asyncSupported = true, value = "/AsyncServlet")
  其次,重要的部分实际上是隐藏在下面这行代码调用中的。
  Work.add(request.startAsync());
  整个请求处理都被委托给了Work类。请求上下文是通过AsyncContext实例来保存的,它持有容器提供的请求与响应对象。
  现在来看看第2个,也是更加复杂的类,Work类实现了ServletContextListener接口。进来的请求会在该实现中排队等待通知,通知可能是上面提到的拍卖中的竞标价,或是所有请求都在等待的群组聊天中的下一条消息。
  当通知到达时,我们这里依然是通过Thread.sleep()让线程睡眠2,000ms,队列中所有被阻塞的任务都是由一个工作线程来处理的,该线程负责编辑与发送响应。相对于阻塞成百上千个线程以等待外部通知,我们通过一种更加简单且干净的方式达成所愿,通过批处理在单独的线程中处理请求。
  还是让结果来说话吧,测试配置与方才的示例一样,依然使用Tomcat 7.0.24的默认配置,测试结果如下所示:
  平均响应时间:265ms
  最快响应时间:6ms
  最慢响应时间:2,058ms
  吞吐量:1,965个请求/秒
  虽然说这个示例很简单,不过对于实际项目来说通过这种方式依然能获得类似的结果。
  在将所有的Servlet改写为异步Servlet前,请容许我多说几句。该解决方案非常适合于某些应用场景,比如说群组通知与拍卖价格通知等。不过,对于等待数据库查询完成的请求来说,这种方式就没有什么必要了。像往常一样,我必须得重申一下——请通过实验进行度量,而不是瞎猜。
  对于那些不适合于这种解决方案的场景来说,我还是要说一下这种方式的好处。除了在吞吐量与延迟方面带来的显而易见的改进外,这种方式还可以在大负载的情况下优雅地避免可能出现的线程饥饿问题。
  另一个重要的方面,这种异步处理请求的方式已经是标准化的了。它不依赖于你所使用的Servlet API 3.0,兼容于各种应用服务器,如Tomcat 7、JBoss 6或是Jetty 8等,在这些服务器上这种方式都可以正常使用。你不必再面对各种不同的Comet实现或是依赖于平台的解决方案了,比如说Weblogic FutureResponseServlet。
  就如本文一开始所提的那样,现在的Java Web项目很少会直接使用Servlet API进行开发了,不过诸多的Web MVC框架都是基于Servlet与JSP标准实现的,那么在你的日常开发中,是否使用过出现多年的Servlet API 3.0,使用了它的哪些特性与API呢?

posted @ 2014-06-09 09:59 顺其自然EVO 阅读(212) | 评论 (0)编辑 收藏

抽离式的测试分析

 一.粗放式分析
  1.存储过程的运行是否正常:编译成功,能运行,且有结果。
  2.存储过程的参数调用正确:能运行成功。
  3.存储过程的返回结果正确:查询,更新或删除的结果是否正确,且返回正常。
  4.存储过程的性能是否达到要求:在可接受的时间内能运行完成并成功。
  二.抽离式分析
  1. 外模式:基于业务场景的分析
  多种业务场景可以映射到一个程序中的等价类逻辑上(即内模式)。
  所以,挑选比较特殊的业务场景,从结果表和程序逻辑层面去印证。
  2. 概念模式:基于结果表的分析
  结果表是对业务场景的概念化和集合化。从结果表数据的类型分析可以延伸出各种业务场景(即外模式)。
  所以,从结果表数据的分类可以反推出对业务场景的覆盖率。
  3. 内模式:基于程序实现的分析
  程序内部实现的逻辑是各种业务场景的抽象,最终形成结果表(即概念模式)。
  所以,对程序逻辑分支的分析可以评估对业务场景的抽象覆盖程度。

posted @ 2014-06-09 09:57 顺其自然EVO 阅读(187) | 评论 (0)编辑 收藏

JMeter中使用Selenium进行测试

JMeter是使用非常广泛的性能测试工具,而Selenium是ThroughtWorks 公司一个强大的开源Web 功能测试工具。Jmeter和Selenium结合使用,就可以实现对网站页面的自动化性能测试。
  这里有两种方式在Jmeter中调用Selenium测试用例。可以使用 Jmeter JUnit sampler 或者 BeanShell sampler,后一种方式包含了Selenium client 和 server,因此不需要单独启动Server。
  方法一
  准备工作:
  将文件selenium-server-standalone-2.*.jar拷贝到JMeter类库安装目录%JMETER_HOME%/lib/下,手动启动Selenium server。
  Jmeter JUnit sampler
  将Selenium测试用例打包成.jar文件,并拷贝到%JMETER_HOME%/lib/junit/目录下,注意:测试用例应该继承TestCase or SeleniumTestCase类,从而允许JMeter获取到该测试用例,且测试方法名称应该以test开头。
  在Jmeter中创建test group和JUnit sampler测试计划,并在JUnit sampler中选择测试用例的名称,选择测试方法并运行。当然还可以根据需求设置其他参数。
  一个测试用例例子:
package com.example.tests;
import com.thoughtworks.selenium.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.After;
public class selenium extends SeleneseTestCase {
@Before
public void setUp() throws Exception {
. . .
}
@Test
public void testSelenium_test() throws Exception {
. . .
}
@After
public void tearDown() throws Exception {
selenium.stop();
}
}
  方法二
  准备工作:
  将文件selenium-server-standalone-2.*.jar拷贝到JMeter类库安装目录%JMETER_HOME%/lib/下,不需要启动Selenium server.
  Jmeter JUnit sampler
  将Selenium测试用例打包成.jar文件,并拷贝到%JMETER_HOME%/lib/junit/目录下,注意:测试用例应该继承TestCase or SeleniumTestCase类,从而允许JMeter获取到该测试用例,且测试方法名称应该以test开头。
  在Jmeter中创建test group和JUnit sampler测试计划,并在JUnit sampler中选择测试用例的名称,选择测试方法并运行。当然还可以根据需求设置其他参数。
  一个测试用例例子:
import junit.framework.TestCase;
import org.openqa.selenium.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.After;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
public class selenium extends TestCase {
WebDriver driver;
@Before
public void setUp() {
FirefoxProfile profile = new FirefoxProfile();
. . .
driver = new FirefoxDriver(profile);
}
@Test
public void testSelenium_test() throws Exception {
. . .
}
@After
public void tearDown() {
driver.quit();
}
}

posted @ 2014-06-05 13:49 顺其自然EVO 阅读(2981) | 评论 (0)编辑 收藏

Spring2.5+JUnit4单元测试

要求:
  JDK1.5以上(因为Junit4是用注解来实现的)
  需要的包
  spring-2.5.jar
  junit-4.4.jar
  spring-test.jar
  测试
package user;
import static org.junit.Assert.fail;
import java.util.Date;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
import com.sample.model.user.User;
import com.sample.service.user.IUserService;
/** 设置要加载的配置文件 */
@ContextConfiguration(
locations={
"classpath:spring/persistenceContext.xml",
"classpath:spring/aopContext.xml",
"classpath:spring/daoContext.xml",
"classpath:spring/serviceContext.xml"
}
)
/** 设置是否回滚数据 */
@TransactionConfiguration(defaultRollback = false)
public class UserTest extends AbstractTransactionalJUnit4SpringContextTests{
/** 设置自动注入的属性 */
@Autowired
private IUserService userService;
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
@Rollback(false)
public void testSaveUser() {
User user=new User();
user.setUsername("zhoujun");
user.setCreatetime(new Date());
userService.saveUser(user);
}
@Test
public void testGetUserById() {
User user=userService.getUserById("1");
System.out.println(user.getUsername());
System.out.println(user.getCreatetime());
}
}
 有关Junit4中注解的说明如下:
  @ContextConfiguration 用来指定加载的Spring配置文件的位置,会加载默认配置文件
  例如下例会加载:classpath:/com/example/MyTest-context.xml文件
package com.example;
@ContextConfiguration
public class MyTest {
// class body...
}
@ContextConfiguration 注解有以下两个常用的属性:
locations:可以通过该属性手工指定 Spring 配置文件所在的位置,可以指定一个或多个 Spring 配置文件。如下所示:
@ContextConfiguration(locations={“xx/yy/beans1.xml”,” xx/yy/beans2.xml”})
inheritLocations:是否要继承父测试用例类中的 Spring 配置文件,默认为 true。如下面的例子:
@ContextConfiguration(locations={"base-context.xml"})
public class BaseTest {
// ...
}
@ContextConfiguration(locations={"extended-context.xml"})
public class ExtendedTest extends BaseTest {
// ...
}
  如果 inheritLocations 设置为 false,则 ExtendedTest 仅会使用 extended-context.xml 配置文件,否则将使用 base-context.xml 和 extended-context.xml 这两个配置文件。
  在使用所有注释前必须使用@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境
  Spring框架在org.springframework.test.annotation 包中提供了常用的Spring特定的注解集,如果你在Java5或以上版本开发,可以在测试中使用它。
  @IfProfileValue
  提示一下,注解测试只针对特定的测试环境。 如果配置的ProfileValueSource类返回对应的提供者的名称值, 这个测试就可以启动。这个注解可以应用到一个类或者单独的方法。
  @IfProfileValue(name=”java.vendor”, value=”Sun Microsystems Inc.”)
  public void testProcessWhichRunsOnlyOnSunJvm() {
  // some logic that should run only on Java VMs from Sun Microsystems
  }
  同时@IfProfileValue可配置一个值列表 (使用OR 语义) 来在JUnit环境中获得TestNG的测试组支持。 看下面的例子:
  @IfProfileValue(name=”test-groups”, values={”unit-tests”, “integration-tests”})
  public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
  // some logic that should run only for unit and integration test groups
  }
  @ProfileValueSourceConfiguration
  类级别注解用来指定当通过@IfProfileValue注解获取已配置的profile值时使用何种ProfileValueSource。 如果@ProfileValueSourceConfiguration没有在测试中声明,将默认使用SystemProfileValueSource。
  @ProfileValueSourceConfiguration(CustomProfileValueSource.class)
  public class CustomProfileValueSourceTests {
  // class body…
  }
  @DirtiesContext
  在测试方法上出现这个注解时,表明底层Spring容器在该方法的执行中被“污染”,从而必须在方法执行结束后重新创建(无论该测试是否通过)。
  @DirtiesContext
  public void testProcessWhichDirtiesAppCtx() {
  // some logic that results in the Spring container being dirtied
  }
  @ExpectedException
  表明被注解方法预期在执行中抛出一个异常。预期异常的类型在注解中给定。如果该异常的实例在测试方法执行中被抛出, 则测试通过。同样的如果该异常实例没有在测试方法执行时抛出,则测试失败。
  @ExpectedException(SomeBusinessException.class)
  public void testProcessRainyDayScenario() {
  // some logic that should result in an Exception being thrown
  }
  @Timed
  表明被注解的测试方法必须在规定的时间区间内执行完成(以毫秒记)。如果测试执行时间超过了规定的时间区间,测试就失败了。
  注意该时间区间包括测试方法本身的执行,任何重复测试(参见 @Repeat),还有任何测试fixture的set up或tear down时间。
  Spring的@Timed注解与JUnit 4的@Test(timeout=...)支持具有不同的语义。 特别地,鉴于JUnit 4处理测试执行超时(如通过在一个单独的线程中执行测试方法)的方式, 我们不可能在一个事务上下文中的测试方法上使用JUnit的@Test(timeout=...)配置。因此, 如果你想将一个测试方法配置成计时且具事务性的, 你就必须联合使用Spring的@Timed及@Transactional注解。 还值得注意的是@Test(timeout=...)只管测试方法本身执行的次数,如果超出的话立刻就会失败; 然而,@Timed关注的是测试执行的总时间(包括建立和销毁操作以及重复),并且不会令测试失败。
  @Timed(millis=1000)
  public void testProcessWithOneSecondTimeout() {
  // some logic that should not take longer than 1 second to execute
  }
  @Repeat
  表明被注解的测试方法必须重复执行。执行的次数在注解中声明。
  注意重复执行范围包括包括测试方法本身的执行,以及任何测试fixture的set up或tear down。
  @Repeat(10)
  public void testProcessRepeatedly() {
  // …
  }
  @Rollback
  表明被注解方法的事务在完成后是否需要被回滚。 如果true,事务将被回滚,否则事务将被提交。 使用@Rollback接口来在类级别覆写配置的默认回滚标志。
@Rollback(false)
public void testProcessWithoutRollback() {
// …
}
@NotTransactional
出现该注解表明测试方法必须不在事务中执行。
@NotTransactional
public void testProcessWithoutTransaction() {
// …
}
Spring TestContext Framework还支持下面这些非特定于测试的注解,并且保持其语义不变。
@Autowired
@Qualifier
@Resource (javax.annotation)如果JSR-250可用
@PersistenceContext (javax.persistence)如果JPA可用
@PersistenceUnit (javax.persistence)如果JPA可用
@Required
@Transactional
@TestExecutionListeners
 定义类级别的元数据,TestExecutionListeners会使用TestContextManager进行注册。 通常,@TestExecutionListeners与@ContextConfiguration会搭配使用。
@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
public class CustomTestExecutionListenerTests {
// class body...
}
  @TransactionConfiguration
  为配置事务性测试定义了类级别的元数据。特别地,如果需要的PlatformTransactionManager不是“transactionManager”的话, 那么可以显式配置驱动事务的PlatformTransactionManager的bean名字。此外, 可以将defaultRollback标志改为false。通常, @TransactionConfiguration与@ContextConfiguration搭配使用。
@ContextConfiguration
@TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
public class CustomConfiguredTransactionalTests {
// class body...
}
@BeforeTransaction
表明被注解的public void方法应该在测试方法的事务开始之前执行, 该事务是通过@Transactional注解来配置的。
@BeforeTransaction
public void beforeTransaction() {
// logic to be executed before a transaction is started
}
@AfterTransaction
表明被注解的public void方法应该在测试方法的事务结束之后执行, 该事务是通过@Transactional注解来配置的。
@AfterTransaction
public void afterTransaction() {
// logic to be executed after a transaction has ended
}

posted @ 2014-06-05 13:44 顺其自然EVO 阅读(319) | 评论 (0)编辑 收藏

使用Selenium/Ant做Web应用远程自动化测试

  Client端主要是通过一个ant build文件来启动JUnit的TestCase的,进而启动TestCase中的test方法,连接并激活server端进行自动化测试。Client端核心测试单元的代码如下:
package com.tail.p2test;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;
public class DemoTest extends TestCase {
private Selenium selenium;
public void setUp() throws Exception {
String url = "http://localhost:8080/";
selenium = new DefaultSelenium("localhost", 4444, "*chrome", url);
selenium.start();
}
protected void tearDown() throws Exception {
selenium.stop();
}
public void testNew() throws Exception {
selenium.setTimeout("100000");
selenium.open("/login.action");
selenium.type("username", "admin");
selenium.type("password", "123");
selenium.click("//input[@value='Log In']");
selenium.waitForPageToLoad("100000");
Thread.sleep(10000);
for (int second = 0;; second++) {
if (second >= 60) fail("timeout");
try { if (selenium.isElementPresent("signLabel")) break; } catch (Exception e) {}
Thread.sleep(1000);
}
// omit lines
...
selenium.open("/main.action");
}
}
 当然,应用可以直接在Eclipse中运行,但是为了能更加灵活,我们考虑用ant脚本来控制client的运行,这里使用ant脚本的一个好处就是可以很方便快捷的输出测试报告,在本例中输出报告的目的就是那个report目录咯。
  ant的Build.xml的脚本详细如下:
<?xml version="1.0"?>
<project name="portal" default="junit" basedir=".">
<property name="source.dir" value="src" />
<property name="build.dir" value="build" />
<property name="lib.dir" value="lib" />
<property name="classes.dir" value="${build.dir}/classes" />
<property name="report.dir" value="report" />
<!-- ================================================================== -->
<!-- C L E A N                                                          -->
<!-- ================================================================== -->
<target name="clean">
<delete dir="${classes.dir}" />
<mkdir dir="${classes.dir}" />
<delete dir="${report.dir}" />
<mkdir dir="${report.dir}" />
</target>
<!-- ================================================================== -->
<!-- C O M P I L E                                                      -->
<!-- ================================================================== -->
<target name="compile" depends="clean">
<!-- local project jars -->
<patternset id="lib.includes.compile">
<include name="*.jar" />
</patternset>
<fileset dir="${lib.dir}" id="lib.compile">
<patternset refid="lib.includes.compile" />
</fileset>
<pathconvert targetos="windows" property="libs.compile" refid="lib.compile" />
<!-- compile -->
<javac srcdir="${source.dir}" destdir="${classes.dir}" classpath="${libs.compile}" includes="**/*.java" debug="true">
</javac>
</target>
<!-- ================================================================== -->
<!-- J U N I T                                                          -->
<!-- ================================================================== -->
<target name="junit" depends="compile">
<junit printsummary="on" fork="true" haltonfailure="false" failureproperty="tests.failed" showoutput="true">
<classpath>
<pathelement path="${classes.dir}" />
<fileset dir="${lib.dir}">
<include name="**/*.jar" />
</fileset>
</classpath>
<formatter type="xml" />
<batchtest todir="${report.dir}">
<fileset dir="${classes.dir}">
<include name="**/*Test.*" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${report.dir}">
<fileset dir="${report.dir}">
<include name="TEST-*.xml" />
</fileset>
<report format="frames" todir="${report.dir}" />
</junitreport>
<fail if="tests.failed">
</fail>
</target>
</project>
  以后,你只需要在work目录下执行一个简单的 ant 命令就能轻松运行整个测试了。

posted @ 2014-06-05 13:40 顺其自然EVO 阅读(325) | 评论 (0)编辑 收藏

Linux静态库和动态库的分析及实现

 1.什么是库
  在windows平台和linux平台下都大量存在着库。
  本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。
  由于windows和linux的本质不同,因此二者库的二进制是不兼容的。
  本文仅限于介绍linux下的库。
  2.库的种类
  linux下的库有两种:静态库和共享库(动态库)。
  二者的不同点在于代码被载入的时刻不同。
  静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。
  共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。
  3.库存在的意义
  库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。
  现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
  共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
  4.库文件是如何产生的
  在linux下,静态库的后缀是.a,它的产生分两步:
  Step 1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表。
  Step 2.ar命令将很多.o转换成.a,成为静态库。
  动态库的后缀是.so,它由gcc加特定参数编译产生。
  例如:
  $ gcc -fPIC -c *.c
  $ gcc -shared -Wl,-soname, libfoo.so.1 -o libfoo.so.1.0 *.
  5.库文件是如何命名的,有没有什么规范
  在linux下,库文件一般放在/usr/lib 下,
  静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称
  动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号,minor是副版本号。
  6.如何知道一个可执行程序依赖哪些库
  ldd命令可以查看一个可执行程序依赖的共享库,
  例如# ldd /bin/lnlibc.so.6
  => /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2
  => /lib/ld- linux.so.2 (0×40000000)
  可以看到ln命令依赖于libc库和ld-linux库
  7.可执行程序在执行的时候如何定位共享库文件
  当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径
  此时就需要系统动态载入器(dynamic linker/loader)。
  对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib目录找到库文件后将其载入内存。第5步:由.o文件创建动态库文件
  动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyhello.so。用gcc来创建动态库。
  在系统提示符下键入以下命令得到动态库文件libmyhello.so。
  # gcc -shared -fPCI -o libmyhello.so hello.o
  #
  我们照样使用ls命令看看动态库文件是否生成。
  # ls
  hello.c hello.h hello.o libmyhello.so main.c
  #
  第6步:在程序中使用动态库
  在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件,再运行它看看结果。
  # gcc -o hello main.c -L. -l myhello
  # ./hello
  ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object
  file: No such file or directory
  #
  哦!出错了。快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中,再试试。
  # mv libmyhello.so /usr/lib
  # ./hello
  ./hello: error while loading shared libraries: /usr/lib/libhello.so: cannot restore segment
  prot after reloc: Permission denied
  由于SELinux引起,
  # chcon -t texrel_shlib_t /usr/lib/libhello.so
  # ./hello
  Hello everyone!
  #
  成功了。这也进一步说明了动态库在程序运行时是需要的。
  我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?抱着对问题必究到底的心情,来试试看。
  先删除 除.c和.h外的 所有文件,恢复成我们刚刚编辑完举例程序状态。
  # rm -f hello hello.o /usr/lib/libmyhello.so
  # ls
  hello.c hello.h main.c
  #
  在来创建静态库文件libmyhello.a和动态库文件libmyhello.so。
  # gcc -c hello.c
  # ar cr libmyhello.a hello.o
  # gcc -shared -fPCI -o libmyhello.so hello.o
  # ls
  hello.c hello.h hello.o libmyhello.a libmyhello.so main.c
  #
  通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标文件hello,并运行程序hello。
  # gcc -o hello main.c -L. -lmyhello
  # ./hello
  ./hello: error while loading shared libraries: libmyhello.so: cannot open shared object
  file: No such file or directory
  #
  从程序hello运行的结果中很容易知道,当静态库和动态库同名时, gcc命令将优先使用动态库。

posted @ 2014-06-05 13:39 顺其自然EVO 阅读(228) | 评论 (0)编辑 收藏

DB2分区数据库的前滚操作

  DB2分区数据库备份映象文件是通过联机备份产生的,在DB2分区数据库恢复操作结束后,数据库将处于前滚暂挂状态,必须通过前滚操作前滚归档日志,以取消前滚暂挂状态,使数据库最终可用。
  对DB2分区数据库的前滚操作:
  由于新数据库 SAMPNEW 的日志路径下不包含源 SAMPLE 数据库日志路径下的归档日志文件,所以在前滚操作之前,需要将 SAMPLE 的归档日志文件复制到一个特定的路径下,然后在发出前滚命令时,使用 OVERFLOW 选项来指定该路径,以替代 SAMPNEW 的数据恢复日志路径来提供前滚操作要使用的归档日志文件。
  为查找源 SAMPLE 数据库归档日志存放的路径,可利用如下命令:
db2_all "db2 get db cfg for sample" | grep "Path"
Path to log files = /home/db2inst1/db2inst1/NODE0000/SQL00001/SQLOGDIR/
Path to log files = /home/db2inst1/db2inst1/NODE0001/SQL00001/SQLOGDIR/
Path to log files = /home/db2inst1/db2inst1/NODE0002/SQL00001/SQLOGDIR/
Path to log files = /home/db2inst1/db2inst1/NODE0003/SQL00001/SQLOGDIR/
  这里假设将各分区的归档日志文件对应复制到 /sampnew 下,因前滚命令仅可在编目分区上执行,所以在编目分区上前滚到日硬盘数据恢复志文件尾并结束前滚状态的命令应写为:
db2 "rollforward db sampnew to end of logs and complete overflow log path
(/sampnew/NODE0000/SQL00001/SQLOGDIR,
/sampnew/NODE0001/SQL00001/SQLOGDIR on dbpartitionnum 1,
/sampnew/NODE0002/SQL00001/SQLOGDIR on dbpartitionnum 2,
/sampnew/NODE0003/SQL00001/SQLOGDIR on dbpartitionnum 3,
)"
  注:对于0号分区,在 OVERFLOW 选项中不能使用“ON DBPARTITIONNUM 0”的子句,否则会遇到:
  SQL0104N An unexpected token "on" was found following "<identifier>". Expected tokens may include: ")". SQLSTATE=42601的报错,表明命令语法不正确。

posted @ 2014-06-05 13:37 顺其自然EVO 阅读(251) | 评论 (0)编辑 收藏

Java组合与继承之间的选择

 组合和继承都允许在新的类中放置子对象,组合是显示的这样做,而继承则是隐式的做。
  组合技术通常用于想在新类中使用现有类的功能而非他的接口。在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是新类所定义的接口,而非所嵌入对象的接口。为了取得这样效果,需要在新类中嵌入一个现有类的parivate对象。
  有时,允许类的用户直接访问新类中的组合成分是极具意义的;也就是说,将成员对象声明为public。如果成员对象自身都隐藏了具体实现,那么这种做法是安全的。当用户能够了解到你正在组装一组部件,会使得端口更加易于理解。Car对象就是一个很好的例子:

posted @ 2014-06-05 13:36 顺其自然EVO 阅读(267) | 评论 (0)编辑 收藏

JMeter工作基本原理

  JMeter可以作为Web服务器与浏览器之间的代理网关,以便捕获浏览器的请求和Web服务器的响应,这样就很容易地生成性能测试脚本,
  有了性能测试脚本,JMeter就可以通过线程组来模拟真实用户对Web服务器的访问压力,这与LoadRunner
  工作原理基本一致。
  原理图如下:

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

仅列出标题
共394页: First 上一页 103 104 105 106 107 108 109 110 111 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜