热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[置顶]性能测试java协议使用httpclient方法引发的思考

最近手上在进行一个性能测试项目,脚本是java语言使用httpClient实现http请求。并发用户数线程只有40个,但是服务器端启动的线程出现了400多个,是哪里平白无故出现这么多线程呢?肯定是有问

最近手上在进行一个性能测试项目,脚本是java语言使用httpClient实现http请求。并发用户数线程只有40个,但是服务器端启动的线程出现了400多个,是哪里平白无故出现这么多线程呢?肯定是有问题的,于是把线程的堆栈信息打印出来查看,发现大量的TIMED_WAITIND等待状态的线程。

"http-bio-8058-exec-210" daemon prio=10 tid=0x00007f72a805c800 nid=0x4049 waiting on condition [0x00007f729a6b6000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000780dd6cb8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:86)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:
- None

第一时间就排查了服务器端是否对线程池的使用有问题,会自动生成过多的线程,经过排查并没有使用线程池的地方,于是我们转向排查是否脚本的编写问题。


脚本代码如下:


public class Actions
{

public int init() throws Throwable {
return 0;
}//end of init


public int action() throws Throwable {
HttpClient client = new HttpClient();
String url = "http://xxxxx";
GetMethod method = new GetMethod(url);

client.executeMethod(method);
System.out.println(method.getStatusLine());
String respStr1 = method.getResponseBodyAsString();
method.releaseConnection();


return 0;
}//end of action


public int end() throws Throwable {
return 0;
}//end of end
}



这是一个最基础的http请求,我把脚本简化后,只操作一个http请求,检查对服务器端线程的影响。在发起40个用户后,线程达到了200多。可见问题就出现在了HttpClient的方法上。大部分人使用HttpClient都是使用类似上面的事例代码,包括Apache官方的例子也是如此。在性能测试过程中,使用HttpClient一次循环发起大量请求到服务器会使TCP连接被大量占用.

tomcat默认请求保持时间是60s,因此这样的配置就会导致每个链接至少要过60S才会被释放,这样在大量请求访问时就必然会造成链接被占满,请求等待的情况。 虽然我调用了method.releaseConnection();这个方法只是将链接返回给connection manager。如果使用HttpClient client = new HttpClient()实例化一个HttpClient connection manager默认实现是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有个构造函数如下

/** 
* The connection manager created with this constructor will try to keep the
* connection open (alive) between consecutive requests if the alwaysClose
* parameter is set to false. Otherwise the connection manager will
* always close connections upon release.
*
* @param alwaysClose if set true, the connection manager will always
* close connections upon release.
*/
public SimpleHttpConnectionManager(boolean alwaysClose) {
super();
this.alwaysClose = alwaysClose;
}

看方法注释我们就可以看到如果alwaysClose设为true在链接释放之后connection manager 就会关闭链。在我们HttpClient client = new HttpClient()这样实例化一个client时connection manager是这样被实例化的 

this.httpCOnnectionManager= new SimpleHttpConnectionManager();  

因此alwaysClose默认是false,connection是不会被主动关闭的,因此我们就有了一个客户端关闭链接的方法。 
方法一: 
把事例代码中的第一行实例化代码改为如下即可,在method.releaseConnection();之后connection manager会关闭connection 。

HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );  

方法二: 
实例化代码使用:HttpClient client = new HttpClient(); 
在method.releaseConnection();之后加上 

((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();  

shutdown源代码很简单,看了一目了然 

public void shutdown() {  
httpConnection.close();
}

方法三: 
实例化代码使用:HttpClient client = new HttpClient(); 
在method.releaseConnection();之后加上 
client.getHttpConnectionManager().closeIdleConnections(0);此方法源码代码如下: 

public void closeIdleConnections(long idleTimeout) {  
long maxIdleTime = System.currentTimeMillis() - idleTimeout;
if (idleStartTime <= maxIdleTime) {
httpConnection.close();
}
}

将idleTimeout设为0可以确保链接被关闭。 
以上这三种方法都是有客户端主动关闭TCP链接的方法。下面再介绍由服务器端自动关闭链接的方法。 
方法四: 
代码实现很简单,所有代码就和最上面的事例代码一样。只需要在HttpMethod method = new GetMethod("http://www.apache.org");加上一行HTTP头的设置即可 
method.setRequestHeader("Connection", "close"); 
看一下HTTP协议中关于这个属性的定义: 
HTTP/1.1 defines the "close" connection option for the sender to signal that the connection will be closed after completion of the response. For example, 
       Connection: close 
现在再说一下客户端关闭链接和服务器端关闭链接的区别。如果采用客户端关闭链接的方法,在客户端的机器上使用netstat –an命令会看到很多TIME_WAIT的TCP链接。如果服务器端主动关闭链接这中情况就出现在服务器端。 
参考WIKI上的说明http://wiki.apache.org/HttpComponents/FrequentlyAskedConnectionManagementQuestions 
The TIME_WAIT state is a protection mechanism in TCP. The side that closes a socket connection orderly will keep the connection in state TIME_WAIT for some time, typically between 1 and 4 minutes. 
TIME_WAIT的状态会出现在主动关闭链接的这一端。TCP协议中TIME_WAIT状态主要是为了保证数据的完整传输。具体可以参考此文档: 
http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7 

另外强调一下使用上面这些方法关闭链接是在我们的应用中明确知道不需要重用链接时可以主动关闭链接来释放资源。如果你的应用是需要重用链接的话就没必要这么做,使用原有的链接还可以提供性能。

所以我们可以把脚本中HttpClient client = new HttpClient();写在action外面,这样创建连接只创建一次,不用每次循环都创建一个连接。


经过验证,以上方法均有效,所以脚本的写法也会影响到我们的测试结果,所以在设计脚本的过程中,要模拟真实用户的使用场景。

另外在本次测试过程中,还对tomcat的线程配置进行了验证。在server.xml中,

               cOnnectionTimeout="20000" 
maxThreads="800"
redirectPort="8443" URIEncoding="utf-8"/>
如果不配置maxThreads参数,默认是最大200线程,所以也解释了,当我把用户数并发到400时,线程数还是保持在200左右。






推荐阅读
  • AComparisonofjava.net.URLConnectionandHTTPClientSincejava.net.URLConnectionandHTTPClienthav ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 本文总结了初学者在使用dubbo设计架构过程中遇到的问题,并提供了相应的解决方法。问题包括传输字节流限制、分布式事务、序列化、多点部署、zk端口冲突、服务失败请求3次机制以及启动时检查。通过解决这些问题,初学者能够更好地理解和应用dubbo设计架构。 ... [详细]
  • 超文本传输协议HTTP也许是当今互联网上使用的最重要的协议了。尽管java.net包提供了基本通过HTTP访问资源的功能,但它没有提供全面的灵活性和其它很多应用程序需要的功能。HttpClient就 ... [详细]
  • 用httpclient做搜狐邮箱的模拟登录,但是请求结果一直都400,因为是https请求,wireshark也抓不到包
    登录结果{body:,message:BadRequest,status:400}感觉是请求缺少了校验,但是不知道怎么完整的请求过程之前http请求的 ... [详细]
  • 本篇文章给大家分享的是有关如何正确的使用HttpClient方法,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不 ... [详细]
  • Android本地化存储Cookie(针对HttpClient)
    因为最近有人问我怎么保存HttpClient的Cookie,所以这里写下,顺便记录总结吧.当然,有Android网络编程经历的童鞋一看就懂喇~就不多说了,直接上代码: ... [详细]
  • 请先查看上一篇文章HttpClient配置,之后在进行。使用this.myhttp.get(http:192.168.2.139:9002apipatients)方法,读取webapi。因 ... [详细]
  • HttpClient作为官方推荐的http客户端,相比之前的WebClient和WebRequest好用了很多,但默认无法为每个请求单独设置超时,只能给HttpClient设置默认 ... [详细]
  • 如何使用.NET CORE HttpClient
    小编这次要给大家分享的是如何使用.NETCOREHttpClient,文章内容丰富,感兴趣的小伙伴可以来了解一下,希望大家阅读完这篇文章之后能够有所收获。前 ... [详细]
  • 在Java领域,谈到网络编程,可能大家脑海里第一反应就是MINA,NETTY,GRIZZLY等优秀的开源框架。没错,不过在深入探究这些框架之前,我们需要先从最original的技 ... [详细]
  • 参考资料:http:www.systinet.comdocwasp_uddiuddiigpreliminary.html教程中的一个例程,可以下载。来源:竹笋炒肉虽然用telnet这样的程 ... [详细]
  • Eclipse利用HttpClient 写post和get连接到后台
    文件目录如下:第一个包代码如下:packagecn.itcast.login;importcn.itcast.login.service.DataService;importandroid.ap ... [详细]
  • Httpclient.setHttpRequestRetryHandler(requestRetryHandler);***设置重连机制和异常自动恢复处理*privatestaticHt ... [详细]
  • 当使用C++做HTTP客户端时,目前通用的做法就是使用libcurl。其官方网站的地址是http:curl.haxx.se,该网站主要提供了Curl和libcurl。Curl是命令 ... [详细]
author-avatar
爱旅游的星琴
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有