论坛首页 Java企业应用论坛

glassfish下的性能调优:令人极度困惑的Max Connections参数

浏览 3479 次
精华帖 (1) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-04-29   最后修改:2010-05-06



近日做性能调优,主要是针对web service,运行于glassfish之上。前期通过修改优化代码,基本搞定一些阻碍性能的问题,主要是代码层次的不合理。

之后还是发现性能上不去,而且表现明显不合理:tps上不去,而服务器cpu只停留在10%附近,压力测试的客户端cpu也不高,20%-30%吧。反复thread dump后检查无果,不论是服务器端还是客户端的工作线程都算正常,没有发现线程/锁之类的问题。分析发现主要的症状是服务器端和客户端都压不上去,服务器端工作线程很空闲,客户端则忙于socket通讯及等待服务器返回。

于是开始怀疑问题可能出现在网络通讯上,一边跑压力测试,一边用netstat命令查看socket状态,很快发现问题,有大量多大数k的socket连接出现。感觉不正常,因为应该用的是长连接,安所正常情况socket连接数应该近似等于并发的线程数。测试工具为客户端soapUI,直接连接到运行在glassfish上的web service.按说soapUI是支持长连接的,glassfish也是支持长连接的。

做了一下验证,只开一个工作线程,跑了几个请求,通过抓包工具发现的确是只建立了一个连接,后面的请求都是跑在同一个socket连接上。试着增加http header Connection: Keep-Alive,发现和默认没有这个参数时表现一致。故意设置为Connection: close,则每次请求都是重新建立连接。因此排除http 长短连接的问题。

继续回头看socket状态,发现出现的多达数k的socket有很多都是处于TIME_WAIT状态,只有少数处于正常的ESTABLISHED状态。TIME_WAIT意味着是服务器端主动要求close socket的,在长连接并且不断有请求的情况下,服务器为什么会如此频繁的关闭连接呢?

试着只开一个压力测试的工作线程,tps大概100+的情况下看服务器端的socket情况,很快发现问题:先是建立一个socket,ESTABLISHED状态,然后大概2s左右重新建立一个新的socket,原有的这个状态转为TIME_WAIT,之后每隔2-3秒左右,都会有上诉的情况出现----原有连接被放弃,重建新的连接。这样socket就成了1 + n的状态:1个ESTABLISHED + n个TIME_WAIT,一定时间后TIME_WAIT的socket开始逐个消失。

将压力测试的工作线程加到100之后,上述情况开始变的极度激烈,大量TIME_WAIT的socket被建立,数目直接上到1w,2w乃至36000,之后开始偶尔报错说无法连接。

问题基本就定位在这里了,为什么明明建立好了长连接,服务器端确总是会不断的关闭这些长连接导致无数的TIME_WAIT?

试着查找资料,调整参数并反复测试,最终发现和两个参数有关:

1. http.maxConnections

glassfish官网说明
https://metro.dev.java.net/guide/HTTP_Persistent_Connections__keep_alive_.html

HTTP keep - alive behavior can be controlled by the http.keepAlive ( default true ) and http.maxConnections ( default 5 ) system properties. For more information, see Networking Properties


进入http://java.sun.com/j2se/1.5.0/docs/guide/net/properties.html 页面查看相关的系统属性,最后还是聚焦在http.maxConnections :

http.maxConnections ( default 5 )
If HTTP keep
- alive is enabled,  this  value is the number of idle connections that will be simultaneously kept alive, per - destination.


从说明上看,应该是max idle connection,和命名maxConnections,maxConnections感觉像是最多容许开这么多长连接。考虑默认值为5明显应该是max idle connection。

后面的测试验证,就是这个参数非常的致命,在修改为200之后,tps直接*2。

返回来分析这个参数,默认最多容许有5个空闲长连接。考虑到100个工作线程,正常应该长连接数目也在100附近,考虑每次请求都要先申请一个连接,用完之后再放回,100个工作线程同时操作,很有可能同时将超过5个的连接返还给连接池。如果服务器简单的判断说多于5个连接然后就立即close并释放长连接,那么就会出现一方面连续释放长连接,一方面因为连接数不够不停的创建新的长连接。

换言之,当100个线程并发在连接池中进行申请连接/返还连接的过程中,连接池内的可用连接数是时刻变化的,实际的数目会有大的波动。而默认的最大空闲参数过小(默认才5)使得这个波动有极大的几率突破限制,从而造成连接池进行不必要的释放所谓过多的“空闲”连接。

glassfish中,对于这个参数的修改,非常简单,在jvm参数中增加新的一项"-Dhttp.maxConnections=250",重启即可。


2. maxKeepAliveRequests

    前面的调整,虽然达到了tps * 2的良好效果,但是使用netstat查看socket时,还是发现有非常多的TIME_TIME状态的socket,只是数目没有原来那么直上3w那么夸张,大概稳定在2000附近。

    看来还是有其他的原因的,重新回头看当时只开一个线程测试的场景:一个线程连续提交,会出现1个ESTABLISHED + n个TIME_WAIT。

    感觉上像是一个长连接上只要跑一段时间或者一定的请求,socket就会被服务器端关闭。修改测试方法,让每次请求之间等待一段时间,降低tps,发现关闭连接的时间间隔大为增加。

    后来google到maxKeepAliveRequests这个参数,对于tomcat,apache等服务器都有支持,解释如下:

maxKeepAliveRequests:
The maximum number of HTTP requests which can be pipelined until the connection is closed by the server. Setting 
this  attribute to  1  will disable HTTP / 1.0  keep - alive, as well as HTTP / 1.1  keep - alive and pipelining. Setting  this  to  - 1  will allow an unlimited amount of pipelined or keep - alive HTTP requests. If not specified,  this  attribute is set to  100 .



    随即google到grizzly也有类似的系统参数可以设置这个maxKeepAliveRequests

- Dcom.sun.enterprise.web.connector.grizzly.maxKeepAliveRequests =- 1


    使用关键字"glassfish maxKeepAliveRequests",发现glassfish还是有支持这个参数的,但是找不到具体设置的方法。后来在glassfish的控制台-> Configuration -> http service -> Keep Alive 下
发现了一个Max Connections参数,默认值250,解释为"Maximum number of connections in the Keep-Alive mode",和maxKeepAliveRequests似乎完全不是一回事。

    但是试着将合格参数修改为2500之后,非常惊讶的发现,见效了!单线程测试长连接释放的速度明显放慢,大体算了一下时间间隔和tpc,无论是之前的默认250还是现在新修改的2500都和测试结果
很合拍。开到100个线程测试,发现原有的2000附近的TIME_WAIT连接被降低到了大概300附近,明显改观。后面发现,可以用下面的参数直接设置:
asadmin set --user admin --passwordfile passwords.txt --port 47348 "server.http-service.keep-alive.max-connections=2500"

    这里就有点奇怪了,从测试结果来看,这个参数的表现和maxKeepAliveRequests参数的功能是一致的,但是这个参数明明叫做Max Connections,而且旁边的注释"Maximum number of connections in the Keep-Alive mode"也证明了这点。很令人费解,并且这里的Max Connections前面的http.maxConnections有重名嫌疑而作用明显不同。

 

下面是一个简单的列表,其他情况相同下,分别修改者两个参数前后的对比:

 

keep-alive.max-connections   http.maxConnections     test result     
250                                      5                                  TPS=650-700   TIME-WAIT=32600       
250                                      200                              TPS=1200        TIME-WAIT=300        
2500                                    5                                  TPS=650-700   TIME-WAIT=32600    
2500                                    200                              TPS=1200        TIME-WAIT=300  

    最终的结果,还是比较理想的,修改了上述两个参数之后,cpu终于压上去了,tps也有了巨大的提升,而且TIME_WAIT的连接也大为减少。但是这两个参数的名称,注释和实际测试中的效果,都有名不副实的感觉,令人困惑。

 

后续更新:
1. 经同事提醒,有新的发现,maxKeepAliveRequests得以确认

    http://docs.sun.com/app/docs/doc/820-4343/abefk?a=view
     这里是sun的官方资料,其中对
Max Connections 参数解释如下:

Max Connections

Max Connections controls the number of requests that a particular client can make over a keep
-alive connection. The range is any positive integer, and the default is 256.

Adjust 
this value based on the number of requests a typical client makes in your application. For best performance specify quite a large number, allowing clients to make many requests. 


    因此可见这个"max connections"参数的确就是通常意义上的"maxKeepAliveRequests"。这里sun的命名不大合适,容易造成误解。

   发表时间:2010-05-06  
看LZ几个帖,胜读十年书.膜拜了.
0 请登录后投票
   发表时间:2010-05-06  
不知云 写道
看LZ几个帖,胜读十年书.膜拜了.


您太客气了
0 请登录后投票
   发表时间:2010-05-07  
LZ这种叙事手法很受用,身临其境的走完一回调优的过程,可以边看文章边自己思考然后再跟下文对比,同时也学到不少分析问题方法,精华了.希望楼主还能保持这样的行文风格,如果能将分析过程详加描述就更好了(而不是像现在这样经过xxx思考,经过xx分析,经过xx考证得知...)
0 请登录后投票
   发表时间:2010-05-07   最后修改:2010-05-07
Joo 写道
LZ这种叙事手法很受用,身临其境的走完一回调优的过程,可以边看文章边自己思考然后再跟下文对比,同时也学到不少分析问题方法,精华了.希望楼主还能保持这样的行文风格,如果能将分析过程详加描述就更好了(而不是像现在这样经过xxx思考,经过xx分析,经过xx考证得知...)


“分析过程详加描述, 这个主要是涉及到篇幅的问题,我感觉一个帖子如果超过3页,能坚持看下来的人基本就很少了。所以一些细节分析就只好略过。另外一个问题就是时间,现在的这个长度的blog都要花了1-2个多小时,如果再长时间就更多了。

另外有一些是基础知识,如果详细说就有走题的风险:比如怎么用netstat看连接,TIME_WAIT什么意思,为什么max idel设置为5会导致高并发下池的动荡。前两个google一下无数资料,后者稍加思考就能理解。所以就不深入了。

另外感谢你的鼓励

至于你所说的叙事手法,其实和我的工作习惯有关。我习惯于在分析问题的时候就同时随手记录一些资料,很明显开始肯定是问题的症状,然后分析,想办法根据迹象找到可能的原因,有时可能只是猜测和怀疑,然后试着去验证,这个过程可能进行多次,因为通常不是一次就能搞定的。期间记录一些简单的资料以备后面分析对比。

最后结果出来之后,验证ok,再把前后的这些简单记录整理一下,加上一些说明和分析,基本就出来你目前看到的文章。文体比较简单直接,基本按照实际工作流程下来,真实感比较好,但是艺术性基本为0.
0 请登录后投票
   发表时间:2010-06-03  
最近我所在的项目组也在用glassfish,不知道什么时候有空向你请教下
0 请登录后投票
   发表时间:2011-03-02  
我们的项目也用到了glassfish做异步消息传输,可是每连接一台机器(仅仅是接收消息),就占了9个连接,唉,也不知道该怎么解决
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics