200 likes | 320 Views
实验室实习报告. 刘澜涛 00848200. 收获. java RMI机制 Jmeter测试工具 java 线程池机制 工作进展情况. java RMI机制. java RMI机制通俗的说就是客户端利用服务器端对象的替身(Stub),像调用本地对象的方法一样来调用服务器端对象的方法。 服务器段方法执行完毕后,客户端像获得本地对象参数一样获得服务器端返回的参数 RMI机制为开发者屏蔽了底层的通信细节。. 编写java RMI程序. 远程对象接口 需要继承Remote类 服务器端远程对象实现 实现远程对象接口的方法 此类中实现的方法在服务器端执行
E N D
实验室实习报告 刘澜涛 00848200
收获 • java RMI机制 • Jmeter测试工具 • java 线程池机制 • 工作进展情况
java RMI机制 • java RMI机制通俗的说就是客户端利用服务器端对象的替身(Stub),像调用本地对象的方法一样来调用服务器端对象的方法。 • 服务器段方法执行完毕后,客户端像获得本地对象参数一样获得服务器端返回的参数 • RMI机制为开发者屏蔽了底层的通信细节。
编写java RMI程序 • 远程对象接口 • 需要继承Remote类 • 服务器端远程对象实现 • 实现远程对象接口的方法 • 此类中实现的方法在服务器端执行 • 服务器端程序 • 利用Naming.bind注册服务器端对象
编写java RMI程序 • 客户端程序 • 通过Naming.lookup()查找指定服务器上的对象 • 查找到对象后则可直接调用该对象的方法 • 客户端代码存根 • 利用rmic生成服务器端的代码存根*_Stub.class
Jmeter测试工具 • Jmeter测试工具是Apache公司开发的基于java的开源压力测试工具,是100%用java实现的。 • 其利用大量的客户端线程来模拟大量的客户端请求,从而进行压力测试。
利用Jmeter进行测试 • 建立线程组(Thread Group) • Thread Group是任何一个测试计划的开始点,可以设置线程数量,建立所有线程的最短时间,每一个线程循环的次数 • 建立取样器(Sampler) • Sampler 告知JMeter 发送请求到server 端 • JMeter自带的Sampler有FTP、HTTP、JDBC、Java Object、LDAP、SOAP/XML-RPC、Web Service • 可以利用java request sampler来实现自己的取样器,我的RMI测试就是利用JMeter的API自己实现取样器来实现的。
利用Jmeter进行测试 • 建立监听器(Listener) • 用来统计测试结果。 • JMeter中有大量的自带监听器,可以方便地生成测试数据图表。 • 对于复杂的测试计划还可使用controller等工具来实现复杂的测试逻辑。
Java线程池机制 • Java的线程池类为java.util.concurrent.ThreadPoolExecutor • ThreadPoolExecutor的常用构造方法为: • ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
Java线程池机制 参数作用: • corePoolSize: 线程池维护线程的最少数量 • maximumPoolSize:线程池维护线程的最大数量 • keepAliveTime: 线程池维护线程所允许的空闲时间 • unit: 线程池维护线程所允许的空闲时间的单位 • workQueue: 线程池所使用的缓冲队列 • handler: 线程池对拒绝任务的处理策略
Java线程池机制 • 当一个任务通过execute(Runnable)方法欲添加到线程池时: • 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。 • 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
java线程池机制 • l 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。 • l 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。 • l 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
Java线程池机制 • 也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。 • 通过向ThreadPoolExecutor的构造方法中传入不同的参数,我们可以建立各种各样的线程池。
工作进展情况 • 第一阶段自己实现多线程客户端程序,进行压力测试,得到的初步结论是: • RMI的线程池大小非固定 • 当客户端请求过多时,服务器端会拒绝客户端的链接 • 存在问题:手动实现的客户端测试程序可信度不高,需利用工具进行测试。
工作进展情况 • 第二阶段工作,利用JMeter压力测试工具,自己实现RMI取样器,对RMI服务器进行压力测试,测试结论: • RMI线程池大小非固定 • 客户端请求多于1200个后,服务器端不稳定,开始有错误率出现(即出现服务器端拒绝连接的情况) • 明显观察到RMI服务器端的处理线程和连接线程在实现上有所区别:处理线程早于连接线程半分钟左右销毁。
工作进展情况 • 第三阶段工作,通过阅读RMI源代码,比照结果程序取得最终结论。 • Java RMI的线程池传入的参数是: • corePoolSize = 0 • maximumPoolSize = Integer.MAX_VALUE • keepAliveTime = 60000ms • unit = TimeUnit.MILLISECONDS • workQueue = new SynchronousQueue<Runnable>() • threadFactory = new ThreadFactory() {....}
工作进展情况 • 源码与测试结果相符: • 初始情况下线程池中没有线程(corePoolSize == 0) • 当有客户端请求后,线程数量迅速增加(因为工作队列 SynchronousQueue将任务直接提交给线程而不保持它们,是直接提交队列。) • 当客户端请求结束后约1分钟内,若没有新的请求,则线程池中所有线程全部销毁(corePoolSize == 0)
工作进展情况 • 当前最大问题:服务器端为何会拒绝客户端的连接 • 针对该问题的进展: • 经过分析应该是在TCPTransport.java的下述代码中出现了Exception • try { connectionThreadPool.execute( new ConnectionHandler(socket, clientHost)); } catch (RejectedExecutionException e) { closeSocket(socket); tcpLog.log(Log.BRIEF, "rejected connection from " + clientHost); }
工作进展情况 • 而ThreadPoolExecutor.java中的execute方法如下: • public void execute(Runnable command) { • if (command == null) • throw new NullPointerException(); • if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) { • if (runState == RUNNING && workQueue.offer(command)) { • if (runState != RUNNING || poolSize == 0) • ensureQueuedTaskHandled(command); • } • else if (!addIfUnderMaximumPoolSize(command)) • reject(command); // is shutdown or saturated • } • } 其中reject方法抛出Exception
工作进展情况 • 根据RMI线程池的参数配置情况,很可能是在addIfUnderMaximumPoolSize(command)方法中出现了问题 • 这个问题还有待进一步查看源码来解决。