今天有同事反应在性能测试环境cpu load很高有500多,我的分析过程是这样的,先用visualVM连上去观察了下,发现请求都卡在channelsocket的read上面。
这一步是mod_jk的代码,并未真正进入应用代码。所以怀疑是apache和jboss之间出现了为题,为了印证这个猜测,先对jboss直接做压力测试,果然应用正常,load也在正常值。
于是就观察了下httpd.conf和mod_jk.conf.发现两个配置除了用的都是正常值并没有其他特殊设置,在15个并发的情况下,就算有性能问题也不应该是这样的。
Netstat –an观察网络连接,发现有很多的close_wait状态。根据tcp-ip关闭连接的四次握手过程和状态转变,close_wait应该是客户端主动发起的,而服务端被动接受关闭,并且服务端还未向客户端发送FIN请求导致。又仔细看了下httpd.conf中的keepalive设置为off。于是就明白是怎么回事了。
直接抓包看图:
Keepalive为on的时候:
Keepalive为off的时候:
红框框部分可以看到,首先客户端(10.19.14.20)向服务器发送FIN请求seq=484
服务器响应ack=484+1并且发送FIN给客户FIN请求seq=142
客户端响应服务端请求ack=142+1
下面再看下具体的状态转换图:
从这个图中可以看到状态变为close_wait的一方为被动关闭方。再结合刚才的抓包图分析,在httpd.conf设置为keepalive为off的时候,客户端会在请求结束之后主动发出close调用,这个时候服务端变为close_wait状态,但是由于apache程序的某些原因导致不能很好的在繁忙情况下处理完这些请求,导致load升高。
上面分析的都是针对http协议的keepalive设置,并且这个keepalive是需要http协议支持。
下面我们再来稍微看一下tcp层的keepalive参数。
其实tcp协议本身就是面向连接的协议,之所以这样设计是因为这个协议是提供一个高可靠的服务,在连接的基础上我们可以做很多的事情保证可靠性,例如流量的控制(滑动窗口),失败重传的机制等等。只要三次握手完成,那么TCP所谓的连接也就建立了。由于有这个三次握手的过程,所以很多坏人会利用这个过程疯狂往服务器发送SYN包,导致服务器响应不过来而挂掉。
那既然连接已经建立了,为什么还有有keepalive呢,因为连接是有个超时时间的,假如在规定时间内没有数据交换的话,就会超时,所以需要定期发送数据来保持连接,linux操作系统的套接口选项就有个SO_KEEPALIVE可以设置心跳的发送时间(默认2小时),可惜这个参数是针对全局的,你设置了,其他人在这台机器的应用心跳数据发送间隔时间都会改变,所以最好自己写程序来实现心跳。
由此可见,http协议的keepalive参数只不过是为了保持连接的一个实现方式而已,他是在http的头上面定义连接是否需要保持(on/off),和保持的时间(timeout).
最后再说一下长连接的用处:
1) 可以省去频繁建立连接的开销
2) 某些场景省去客户端对服务端的频繁轮询
3) 服务端和客户端可以灵活的来回读写数据,所谓的推和拉都可以基于此实现。
比如所谓的comet听着很炫,也就是长连接上的一些需求实现。但是由于不是所有的浏览器程序都支持长连接,例如IE就不支持流式的AJAX,只能通过例如flash socket程序,或者iframe等猥琐的方式来实现。
以上是小弟因为keepalive引起的乱弹,欢迎各位批评指正。谢谢。
补充2个文档: