Transcript downloading

实验室实习报告
刘澜涛
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)方法
中出现了问题
• 这个问题还有待进一步查看源码来解决。