以 IMS Connect V13 组件的自动化测试案例验证说明
<!-- LEADSPACE_BODY_END --><!-- SUMMARY_BEGIN -->在交易中间件的性能测试中,经常会面临需要模拟多个用户行为的测试场景的问题,并且这些场景中的用户行为通常都是并行的。Java 的多线程编程能有效地模拟这些场景,使测试场景丰富,测试效果较好。本文以一个实际的项目为例,介绍了如何使用 Java 多线程编程来模拟多个用户的随机或有规律的行为。利用 Java 的多线程编程,能较好的测试产品,更早的暴露潜在的问题。
1 评论:
引言
随着信息系统的结构的日益复杂和规模的不断扩大,交易中间件在复杂系统的应用也越来越广。交易中间件作为一个中间层的系统,在接收客户端请求时,通常需要做一些负载控制和用户缓存的一些功能。对于软件测试人员来说,测试交易中间件时,避免不了模拟客户端在高负载情况下的一些有规律或随机的行为。这些测试有时是功能性验证测试 (Functional Verification Test),有时也涉及到性能测试 (Performance Test)。
本文将介绍如何使用 Java 语言编写多线程的自动化测试脚本,并且使用线程池模拟一些特殊的有规律的测试场景。
本文首先会简单的介绍交易中间件及 Java 多线程编程的概念。接着提出项目中遇到的问题。然后就碰到的问题,使用 Java 多线程技术模拟测试场景。解决这个问题后,就类似的问题提出推广的思路。
本示例的必备条件
Java 的多线程应用范围很广,交易中间件的种类也有许多。本文 JDK 的版本是 JDK5,原因是 JDK5 中加入了比较丰富的多线程并发工具。目前,JDK 的最新版本是 JDK7,其中又增加了许多工具包括 Phaser、ThreadLocalRandom、ForkJoinPool、以及 TransferQueue 等,但是如果掌握了 JDK5 的多线程工具,对 JDK7 的工具也一定不会陌生了。
本文的交易中间件是以 IBM Information Management System(IMS)的 TM 为示例,这个示例是为您创建一个学习的场景,当然 Java 的多线程应用的范围非常广泛,不限于这一种交易中间件。在本文的推广部分,也另外假设了一个场景,并加以实现。
如果您也需要以 IMS TM 来测试,请确保提供以下的测试环境。
- 需要在 Windows 上安装的软件:
JDK 1.5(或者更新的版本)
Rational Functional Tester V7.0(或者更新的版本)
- 需要在 IBM z/OS 上安装的环境:
IMS Version 9 (或者更新的版本)
IMS Connect Version 9 (或者更新的版本)
OTMA
TCP/IP
概念介绍
交易中间件
中间件的产品种类很多,根据中间件在系统中所起的作用和采用的技术不同,大致划分为五大类:数据库中间件 (Database Middleware,DM)、远程过程调用中间件 (Remote Procedure Call, RPC)、基于对象请求代理 (Object Request Broker, ORB)、中间件与交易中间件 ( Transaction Processing Monitor, TPM, 也称事务处理中间件 )。
交易中间件是一种复杂的中间件产品,通常是在负载的环境下运用分布式应用的速度和可靠性来实现的。交易中间件向用户提供一系列的服务,包括通信服务、日志服务、系统服务和交易服务等。
交易中间件的通信主要是基于 TCP/IP 的 socket 技术和基于消息传递与排队机制实现的,其通信的过程如图 1 所示:
图 1. 交易中间件通信过程
交易中间件端通常有侦听方法在监听客户端的连接请求,返回一个连接后并生成一个相应的客户服务进程。在接收到客户端的数据后,对数据进行分割、加密、封装成消息包。然后做分发、入队、发送等操作。
Java 多线程编程
从交易中间件的概念中可以看出,如果要模拟客户端使用交易中间件,首先要模拟建立 Socket 连接。接下来,如果需要模拟多个用户的连接,就需运用用 Java 的多线程机制了。
Java 的多线程机制使应用程序能够并发的执行,并且 Java 能够运用同步机制使并发的线程能够互斥的使用共享资源,从而达到线程安全的作用。Java 的多线程的实现方法有两种:1) 通过 Thread 继承。为创建一个线程,最简单的方法就是从 Thread 类继承。2) 通过 Runnable 接口。该方式主要是用来实现特殊功能,如复杂的多继承功能等。Runnable 接口的应用虽然提高了软件开发的灵活度,但是同时也是造成 BUG 的根源之一,所以应根据不同的需求,合适的选取两种方法,两种实现的方法的代码可见代码清单一。
清单 1.Java 多线程实现的两种方式
// 方式一 public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } // 在合适的地方启动线程如下: MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start(); // 方式二 public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } } // 为了启动 MyThread,需要首先实例化一个 Thread,并传入自己的 MyThread 实例 MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start();
在 Java 中,要实现多线程流程控制方法有以下几个:
- 通过 sleep() 来实现 Java 多线程流程控制,可以用于多线程流程控制演示。
- 通过 interrupt() 函数对 Java 多线程进行流程控制。这种控制方法比 sleep() 函数要精确得多,但在用法上与 sleep() 函数不太相同。
- 通过 wait() 和 notify() 来进行流程控制。wait() 方式与 sleep() 方式有相同之处,都是从线程内部对线程进行中断的。不同的是,它没有提供中断时间,其唤醒方式就是 notify() 和 notifyA ll()。
- 通过 join() 来进行流程控制,join()方式结合了 interrupt 和 sleep 方式的优点。在实际设计当中应注意使用 join 方式,因为不恰当的使用可能会打乱线程的流程。
项目面临的问题
项目背景
IBM 的 IMS 和 CICS(1968)是在 IBM 大型机 mainframe 时代最早的交易中间件,它们都采用请求队列管理、检查点机制和批处理的启动机制。本文所要测试的交易中间件正是 IBM 公司的大型数据库产品 IMS 的交易中间件。它提供了交易处理的通信、并发访问控制、事务控制、资源管理和必要的事务监听功能。IMS 的控制及数据流程可见图 2。
图 2. IMS 控制及数据流程
项目需求
在 IMS TM 中,负责 TCP/IP 通信的模块是 IMS Connect,这也是本文测试的核心连接模块。该模块的新增功能包括 TCPIPQ 和 Health Report 功能。TCPIPQ 是一个 socket 连接的缓冲队列,那些暂时没有被接受的连接请求会进入这个队列,它的大小是可控的。Health Report 功能是定时的检查目前的 socket 连接是否达到最大连接数,并且将已连接的比例汇报给相应的控制模块,客户也可以通过命令查看当前已建立 socket 的占有率。
面临的问题
- 在测试这两个新增功能时,由于 IMS Connect 能够建立的最大 Socket 连接数可以达到 65535 个,而最少的数目也有 100。这么多的 socket 数目,如果一个一个的开启客户端程序来发起交易是不现实的。
- 在测试 Health Report 时,当需要测试占有率达到百分之百,即模拟当前的 socket 连接已经达到上限的场景,需要同时建立并发的请求,并且数目要足够多,达到连接上限。
- 在测试 TCPIPQ 时,只有让现有的 socket 连接达到最大连接数,新的连接才能进入 TCPIPQ 队列。如果需要测试队列的出队和入队操作是否正确,需要多个线程有规律的简历 socket 连接。
解决方法及实现细节
为了解决项目面临的测试难题,本文采用 Java 的多线程机制来模拟这三个问题所涉及的场景。由于在本项目中客户端发出的请求需要复杂的处理,所以本文采用实现 Runnable 接口的方法来实现多线程,这样发送请求的类还可以继承其它的工具类来实现复杂的处理。
由于 IMS Connect 所支持的 socket 的数目 (MAXSOC) 可以从 100 到 65535,而 TCPIPQ 队列的大小可以从 50 到 65535,本文在实现客户端多线程请求前,设置 IMS Connect 的 MAXSOC=100 并且设置 TCPIPQ=50。
在这个前提下,本文实现的线程类在演示时,如果创建 100 个线程的客户端连接,就会达到 socket 连接的上限,使 IMS Connect 的 Health 为 0,意味着不能再监听多余的 socket 请求,如果继续发送 50 个请求,这些请求会进入 TCPIPQ 的队列,使这个缓存队列也达到上限。再继续发送请求就会被拒绝,并得到拒绝的提示信息。
如果使用最简单的实现方法,可以参考如下的代码清单 2。
清单 2. 多客户端并发连接的简单实现
class Utility { // 工具类 public byte[] getHeadData(String...parms){ ..... .... } public byte[] getBodyData(String...parms){ ..... .... } public byte[] getFootData(String...parms){ ..... .... } } class SocketClient extends Utility implements Runnable,Test{ // 实现 Runnable 接口 String hostName; int port; public SocketClient(String hostName,int port) { this.hostName=hostName; this.port=port; } // 线程启动后的连接,并发送数据的 public void run() { try { InetAddress inetAddress = InetAddress.getByName(hostName); Socket socket = new Socket(inetAddress,port); OutputStream os = socket.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream(os); byte[] sendHeadData = getHeadData("aaa","bbb","ccc"); byte[] sendBodyData = getBodyData("ddd","eee","fff"); byte[] sendFootData = getFootData("ggg","hhh","iii"); bos.write(sendHeadData); bos.write(sendBodyData); bos.write(sendFootData); bos.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } // 测试的主线程,演示测试的一些场景 public class SocketConnectionDemo { public int connectionNumb; // 连接数 public String hostName;// 连接的 IMS Connect 的域名 public int port;// 连接的端口 public SocketConnectionDemo(int connectionNumb,String hostName, int port){ this.connectionNumb=connectionNumb; this.hostName=hostName; this.port=port; } public static void main(String[] args) { int connectionNumb = 50; String hostName = "ec32181.vmec.svl.com"; int port = 9999; SocketConnectionDemo scd = new SocketConnectionDemo(connectionNumb,hostName,port); } // 最简单的测试场景 public void testScenario1(){ for(int i=1; i<=connectionNumb; i++){ SocketClient sc = new SocketClient(hostName, port); Thread clientT = new Thread(sc,"Client"+i); clientT.start(); } // 测试的具体逻辑 … .. … . } }
从这个代码清单可以看出,testScenario1 是具体的测试场景调用的方法。这个测试场景使用最简单的方法实现线程的启动,并根据具体的 connectionNumb 建立相应的连接数,在这些连接都建立之后,可以执行具体的测试逻辑,测试在峰值情况下的 IMS Connect 的表现,并查看具体的 Health Value。
这个实现也存在一些问题,这些多线程的客户端虽然是并行的执行,但是它们并不是在同一起跑线开始的,因为在主线程中,它们都是一个一个建立的线程,一旦建立就 start 了。为了实现所有线程同时开始,快速使得 IMS Connect 达到峰值,就需要使用线程池技术,这就是 JDK1.5 中新增并发工具类。在此使用了 CountDownLatch 来实现连接数的控制,并且通过一个值为 1 的 CountDownLatch 对象来实现同一起跑线开始的闸门功能。详细代码请查看代码清单 3。
清单 3. 使用 CountDownLatch 实现并发连接
class ThreadTest implements Runnable{ private static CountDownLatch startCdl; // 用于启动所有连接线程的闸门 private static CountDownLatch doneCdl;// 所有连接工作都结束的控制器 public ThreadTest(CountDownLatch startCdl,CountDownLatch doneCdl) { this.startCdl=startCdl; this.doneCdl=doneCdl; } public void run() { try { startCdl.await(); System.out.println( Thread.currentThread().getName()+" has been working!!!!"); // 此处需要代码清单一的那些连接操作 …… .. …… … .. doneCdl.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } } public class CountDownLatchDemo1 { public static void main(String[] args) { CountDownLatch startCdl = new CountDownLatch(1);// 启动的闸门值为 1 CountDownLatch doneCdl = new CountDownLatch(100);// 连接的总数为 100 for(int i=1; i <=100; i ++){ ThreadTest tt = new ThreadTest(startCdl,doneCdl); new Thread(tt,"Thread"+i).start(); } // 记录所有连接线程的开始时间 long start = System.nanoTime(); // 所有线程虽然都已建立,并 start。但只有等闸门打开才都开始运行。 startCdl.countDown(); try { doneCdl.await();// 主线程等待所有连接结束 // 连接达到峰值后,执行一些测试逻辑代码 ...... ...... ...... } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 记录所有连接线程的结束时间 long end = System.nanoTime(); System.out.println("The task takes time(ms): "+(end-start)/100000); } }
应用及验证
在上一节,介绍了整个多线程的实现细节。在本节中,将运行这个多线程的应用,并结合项目的 IMS Connect 服务器,演示连接数是否能像预期一样达到上限,进行峰值的测试。
IMS Connect 目前的版本是 V12,正在开发的版本是 V13。在 V13 中,可以通过一些命令来查看当前的 Health 值,即连接数与上限的比例情况。本文中,设置 IMS Connect 的上限连接数为 100,开放端口为 5,那么在 IMS Connect 开启时,可以通过命令查看得到 Health 值为 95,这意味着目前连接数占有率为 95%。
接下来,开启多线程的连接,并发的连接 95 个连接。IMS Connect 后台会显示连接在不断的增长,当达到峰值时会出现相应的提示信息。
此时,再查看 Health 的值,会发现已经变成了 000,这意味着 IMS Connect 已经达到了峰值。
推广
本文为了实现多个连接线程同时开启并发拼抢连接交易中间件服务器的过程,使用了 JDK1.5 的并发类 CountDownLatch。但是,JDK1.5 以后的版本引入了许多高级的并发特性,充分利用了现代多处理器和多核心系统的功能以编写大规模并发应用程序。主要包含原子量、并发集合、同步器、可重入锁,并对线程池的构造提供了强力的支持。
除了本文使用的 CountDownLatch,还有许多同步器。例如,Semaphore、Barrier、Exchanger、Future 等。为了实现项目后期的连接缓存队列 TCPIPQ 的测试,Exchanger 可能会被使用。Exchanger 可以实现两组线程互相交换一些共享资源的功能。
为了说明 Exchanger 的使用,本文假设一种场景。假设有一个连接缓存队列,有一个连接器负责创建连接,创建的连接会存储在队列里。另外一个释放连接器可以释放连接,从队列里移除连接。连接器每次会随机地创建 1 或 2 个连接。释放连接器只能每次释放 1 个连接。最后肯定会有连接队列满的时候,那时就可以进行连接队列的测试了。
要实现这一种场景,可以使用 Exchanger 来实现。可以查看代码清单 4。
清单 4. 使用 Exchanger 实现连接缓存队列的测试
// 假设建立连接的类是 Connection class Connection{ private String connName; private String ipAddress; public Connection(String connName,String ipAddString) { this.connName=connName; this.ipAddress=ipAddString; } ..... ... } public class ConnectionQueueDemo { // 使用交换器实现连接器与释放连接器之间资源的共享 private static Exchanger<LinkedList<Connection>> exconn = new Exchanger<LinkedList<Connection>>(); // 连接器 public class Connector implements Runnable{ private LinkedList<Connection> connQueue; private String ipAddress; public Connector(LinkedList<Connection> connQueue,String ipAddress) { this.connQueue=connQueue; this.ipAddress=ipAddress; } public void run() { boolean flag=true; while(flag){ // 每次连接随机的 1~2 个连接。 Random random = new Random(); int connNumb = (random.nextInt())%2 + 1; // 得到随机的 1~2 个连接数 if(connNumb > 1){ System.out.println("Connector creates 2 connection!"); }else{ System.out.println("Connector creates 1 connection!"); } for(int i=0; i<connNumb; i++){ Connection conn = new Connection("Connector", getIpAddress()); connQueue.add(conn); } // 休息 1 秒 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e1) { e1.printStackTrace(); } try { // 交换给释放连接器,让释放连接器工作! connQueue =(LinkedList<Connection>) exconn.exchange(connQueue); } catch (InterruptedException e) { e.printStackTrace(); } if(connQueue.size()==10){ System.out.println("The connection queue is full!! The programme is end!"); // 当队列满时,可以加入一些测试逻辑代码 .... ... flag=false; System.exit(0); }else{ System.out.println("After Disconnector, the size of the queue is "+connQueue.size()); } } } public String getIpAddress(){ return ipAddress; } } // 释放连接器 public class Disconnector implements Runnable { private LinkedList<Connection> connQueue; public Disconnector(LinkedList<Connection> connQueue) { this.connQueue=connQueue; } public void run() { boolean flag=true; while(flag){ System.out.println("Disconnector disconnects 1 connection!"); if(!connQueue.isEmpty()) connQueue.remove(0); // 休息 1 秒 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e1) { e1.printStackTrace(); } try { // 交换给连接器,让连接器工作! connQueue =(LinkedList<Connection>) exconn.exchange(connQueue); } catch (InterruptedException e) { e.printStackTrace(); } if(connQueue.size()==0){ System.out.println("There is no connection in the queue!"); }else{ System.out.println( "After Connector, the size of the queue is "+connQueue.size()); } } } } public static void main(String[] args) { LinkedList<Connection> connQueue = new LinkedList<Connection>(); ConnectionQueueDemo connectionQueueDemo = new ConnectionQueueDemoEx(); new Thread(connectionQueueDemo.new Connector(connQueue,"192.168.1.1")).start(); new Thread(connectionQueueDemo.new Disconnector(connQueue)).start(); } }
在 main 函数里是具体的 Demo 实现。新建了连接器和释放连接器两个线程,它们共享一个连接缓存队列。由于,连接器每次随机的连接的连接数要大于释放连机器释放的连接数,所以最后,连接队列会满。后台打印的输出截屏如下:
图 3.Exchanger 实现的多线程 Demo 的后台输出
总结
在交易中间件的测试中,伴随着网络连接的复杂情况,程序员需要模拟不同的场景来测试不同的边界情况。不仅如此,还需要模拟大量客户端连接来测试达到峰值的情况。本文以 IMS 的交易管理器的一个组件 IMS Connect 为例,阐述了如何使用 Java 并发编程的技术来模拟多个客户端发出连接请求的方法。
本文也对 Java 多线程编程做出了一些推广应用。Java 的多线程技术在 JDK1.5 之后有了许多新的特性和扩展,在推广的章节,本文利用了 Exchanger 类来实现了客户端连接和释放的测试场景。
最后,希望通过本文的介绍能对会面临同样问题或类似问题的程序员们提供一些思路,能有利于测试人员更好的测试交易中间件,达到峰值测试的效果。
<!--CMA ID: 851728--><!--Site ID: 10--><!--XSLT stylesheet used to transform this file: dw-document-html-7.0.xsl-->
相关推荐
这些文件可能包含实验指导文档、源代码示例、配置文件模板等资源,帮助学习者逐步完成实验步骤,理解并掌握ActiveMQ在单线程多队列应用中的实现。新建文件夹可能用于存放实验过程中产生的输出文件或日志,便于后续...
在多线程环境下,数据库中间件需要处理并发访问问题。学习Java的synchronized关键字、Lock接口、ReentrantLock等并发工具,以及乐观锁、悲观锁策略,可以帮助你设计出能处理并发请求的中间件。 6. **查询优化** ...
“IBM MQ多线程测试”涉及到利用多线程技术优化消息传递的性能,这需要对IBM MQ的工作原理、Java多线程编程以及并发控制有深入理解。通过精心设计的测试,我们可以评估和改进系统的性能,以满足高并发需求。
1. **Java基础与进阶**:书中可能涵盖了Java语言的基础知识,包括语法、面向对象编程、异常处理、多线程等,并进一步讨论了高级主题如JVM内存管理、性能调优和并发编程策略。 2. **Web框架与Servlet**:在Java Web...
### 性能测试应用中间件调优 在IT领域中,中间件的性能优化是确保应用程序稳定高效运行的关键环节之一。本文将根据给定的文件信息,深入探讨几个关键方面:JVM参数调整、WebLogic Server(WLS)参数与配置优化、...
本书主要聚焦于JAVA技术栈在大型网站系统中的实际应用,涵盖了诸多关键技术和中间件,旨在帮助读者理解和掌握构建高性能、高可用性系统的关键要素。 在JAVA中间件领域,本书可能涵盖以下知识点: 1. **JAVA基础**...
消息中间件(Middleware)是分布式系统架构中的关键组件之一,它是一种软件基础设施,专注于在分布式系统之间发送和接收消息。消息中间件(MOM,Message-oriented middleware)可以提供高效可靠的消息传递机制,实现...
JACOB在实际应用中的场景多种多样,例如自动化测试、文件操作、数据库连接等。它特别适用于那些需要与Windows特定功能集成的Java应用,如Excel操作、Word文档生成、PDF处理等,因为这些通常可以通过Microsoft Office...
Java-COM中间件 JACOB 是一个用于在Java应用程序中与Microsoft COM(Component Object Model)组件进行交互的库。这个库允许Java开发者调用COM组件的功能,使得Java应用能够利用Windows平台上的各种COM对象,例如...
书中可能涉及Java编程基础、面向对象设计原则、多线程编程以及异常处理等。 3. **Web容器与Servlet**:介绍Tomcat、Jetty等Web服务器的工作原理,以及Servlet和JSP在Web开发中的应用,帮助读者理解HTTP协议和动态...
在Java等支持多线程的语言中,线程安全通常涉及到同步机制,如`synchronized`关键字、锁、条件变量等。确保线程安全有助于提高程序的并发性能,避免竞态条件和死锁等问题。在"ThreadSocket"项目中,可能涉及到在多...
- **系统集成**:在Java应用中调用已有的COM组件,实现不同技术栈之间的集成。 总结来说,JACOB是Java开发者在Windows环境下利用COM组件的重要工具,通过它,开发者可以轻松地在Java程序中调用各种Windows特有的...
ATM系统需要处理各种交易,包括查询余额、取款、存款等操作,它涉及到用户认证、数据库访问以及多线程处理等高级特性。通过ATM系统的案例,读者可以学习到如何在Java 3.5环境下实现复杂的业务逻辑。 抽象窗口工具包...
3. 实验三:掌握Intent跳转和Java多线程,通过编写实际应用加深理解。 4. 实验四:使用SQLite数据库,进行数据操作。 最终考核包括考勤、三个实验的完成情况和一个期末考试,全方位评估学生的理论知识和实践能力。
通过实际应用中的测试,验证了框架设计在性能和准确性方面的优势,例如能够实现对返回数据的快速准确查询,且操作耗时较少。这些实证分析结果进一步支持了基于Java数据库应用框架设计的有效性和必要性。 综上所述,...
Java多线程程序死锁检查 JCarder JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 Java...
Java多线程程序死锁检查 JCarder JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 Java...
Java多线程程序死锁检查 JCarder JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 Java...