httpclient使用不当产生大量CLOSE_WAIT的解决过程

近日,我们的k8s+rancher的docker环境出现了一个很奇怪的问题,在没有进行任何操作的情况下,我们的web跑着跑着就突然挂了。导致我们自动化用例全部执行失败。于是我和罗仔开始了以下的排查过程。

首先就是在我们的web上查看日志,发现没有任何抛错。我们的自动化用例开启后,一直再向服务端发起请求,但是服务端返回给客户端的响应都是异常,说明服务端压根没有收到请求。于是我们check了Nginx的配置,没有问题,那是为什么呢,这时我们打开netstat命令

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 

发现服务器有大量的CLOSE_WAIT状态的socket,这时候问题原因找到了,是因为大量的CLOSE_WAIT造成tomcat假死,以至于我们的web无法提供服务。


image.png

如下图:


四次挥手.png

根据tcp四次挥手的状态图可以看出来,这个状态是因为客户端主动发起了关闭socket连接的请求,发送了FIN报文给服务端,此时服务端处于了CLOSE_WAIT状态,但是服务器程序自己没有进一步发出ack 信号,于是导致这个资源一直被程序占着。紧接着我们查询了Nginx的日志,发现出现了大量的499状态码。查看Nginx中499的定义是 “client has closed connection”。说明客户端等的不耐烦了,主动关闭了连接。


Nginx日志.png

于是我们发现,这个时间段内,只有我们获取cookie的微服务在不断地发起http请求,于是我们停止该微服务,观察了一段时间,发现close_wait没有再增长,那么问题的源头就找到了。查看我们微服务的代码发现:

 try {
                response = HttpUtil.processJsonPost(client
                        , context
                        , gotestUrl
                        , headers,bodyData);
                String jsonBody = EntityUtils.toString(response.getEntity(), "UTF-8");
                JSONObject responseObj = JSONObject.parseObject(jsonBody);
                if (responseObj.getInteger("code")== 200){
                    logger.log(Level.INFO,"success! ");
                }else {
                    logger.log(Level.WARNING,"something going wrong! ");
                }
            }catch (Exception ex){
                logger.log(Level.SEVERE, ex.getMessage(), ex);
            }

代码中的http连接都没有关闭,所以导致出现大量的close_wait。修改代码:

            try {
                response = HttpUtil.processJsonPost(client
                        , context
                        , gotestUrl
                        , headers,bodyData);
                String jsonBody = EntityUtils.toString(response.getEntity(), "UTF-8");
                JSONObject responseObj = JSONObject.parseObject(jsonBody);
                if (responseObj.getInteger("code")== 200){
                    logger.log(Level.INFO,"success! already send cookie to gotest");
                }else {
                    logger.log(Level.WARNING,"something going wrong! cannot seccessfully send cookie to gotest");
                }
            }catch (Exception ex){
                logger.log(Level.SEVERE, ex.getMessage(), ex);
            }finally {
                if (null != client){
                    try {
                        client.close();
                    }catch (IOException e) {
                        logger.log(Level.SEVERE, e.getMessage(), e);
                    }
                }
                if (null != response) {
                    try {
                        response.close();
                    }catch (IOException e) {
                        logger.log(Level.SEVERE, e.getMessage(), e);
                    }
                }
            }

重启微服务,果然close_wait没有再增长了,问题解决。
经过这次问题的排查,也给了自己一下警示:
1.代码一定要规范,尤其是在申请资源的部分,写之前就需要注释,不要忘记释放资源;
2.排查问题的时候,需要逐步地去分析,一个一个地排除影响因子,才能更快更准确地定位问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容