zoukankan      html  css  js  c++  java
  • 由于httpClient调用导致的ESTABLISHED过多和 Connection rest by peer 异常

    问题描述:

    生产环境突然之间出现了大量的Connection rest by peer.后来使用netstat -an | grep 服务端口号发现有大量来自A10服务器的ESTABLISHED连接,多的时候单台达到1万多,总连接数达到3万多。后来查看A10服务器发现,连接数来自同一个客户,于是猜测可能是该用户在请求时,每一次都会建立一个新的连接,并且该连接没有释放。

    于是联系该用户,查看他们的建立请求连接的代码:

    发现客户使用了apache的httpClient的method.releaseConnection()来释放资源。经过同事的提醒说这样的释放资源其实是有问题的,于是在网上找到了解决相关介绍:

    先介绍一下,做了哪些工作以及后期的改正工作:

    1.首先在A10限制每一个ip可以访问的连接数,

    2.在A10配置所有的连接请求为短连接

    3.代码层面,通知客户修改其调用程序

    4.在服务层面应该禁止长连接的建立,都保持为短连接。

    以下是httpClient的使用介绍:

    http://www.myexception.cn/internet/1552774.html

    HttpClient引起的TCP连接数高的问题分析

    【问题现象】

    系统上线后出现TCP连接数超过预期阀值,最高值达到8K左右,新上线代码中包含了一文件上传操作,使用的是apache的commons-httpclient包。

    【问题分析】

    1、先确认是否存在连接未关闭问题引起的。

    观察发现,TCP连接数不是一直在增长,而是会有所下降。并且当业务低峰期TCP连接数TCP连接数会降到100左右,这说明TCP连接还是会关闭。

    2、确定居高不下的TCP使用情况

    使用"netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'"命令发现,处于ESTABLISHED状态的连接数最多,在查看了一下处于ESTABLISHED状态的目的IP,基本上都是文件服务器的 IP,这说明还是跟新增加的文件上传操作有关。但是按照代码的逻辑来看,文件上传操作是多线程处理的,一个线程处理一个上传操作,线程池中一共有10个线 程,照此分析正常的话应该有10个左右与文件服务器的链接,不应该出现几千个链接。因此怀疑是连接没有主动释放,而是等待连接超时才开始释放。

    3、为什么会连接超时

    查看了文件上传部分代码,主要代码如下:

    HttpClient client = new HttpClient();
    MultipartPostMethod method = new MultipartPostMethod(config .getUploadInterface());
    try{
        client.executeMethod(method);
    }catch (Exception e){
         throw e;    
    }finally{
         method.releaseConnection();
    }

     从代码里看是已经释放连接了,但是从结果上看没有释放连接,那就产生一个问题,这个地方真的能释放连接吗?我们在释放连接后面增加一行测试代码来看看:

    HttpConnection conn = client.getHttpConnectionManager().getConnection(client.getHostConfiguration());
    System.out.println(conn.isOpen());

     打印出的结果是true,也就是说虽然调用了releaseConnection,但是并没有释放连接!!

    4、分析commons-httpclient相关代码

    现在怀疑是我们使用的方式不对了,继续分析一下commons-https包中相关代码,首先看一下method.releaseConnection()的代码实现:

    public void releaseConnection() {
            try {
                if (this.responseStream != null) {
                    try {
                        // FYI - this may indirectly invoke responseBodyConsumed.
                        this.responseStream.close();
                    } catch (IOException ignore) {
                    }
                }
            } finally {
                ensureConnectionRelease();
            }
        }
    private void ensureConnectionRelease() {
            if (responseConnection != null) {
                responseConnection.releaseConnection();
                responseConnection = null;
            }
        }

     经过debug发现responseStream为null,并且responseConnection也为null,这样改调用就没有实际意义。那么我们应该怎么来释放连接呢?

    5、继续分析代码

    我们发现在org.apache.commons.httpclient.HttpMethodDirector类的第208行已经在finally中释放连接了:

    finally {
                if (this.conn != null) {
                    this.conn.setLocked(false);
                }
                // If the response has been fully processed, return the connection
                // to the pool.  Use this flag, rather than other tests (like
                // responseStream == null), as subclasses, might reset the stream,
                // for example, reading the entire response into a file and then
                // setting the file as the stream.
                if (
                    (releaseConnection || method.getResponseBodyAsStream() == null) 
                    && this.conn != null
                ) {
                    this.conn.releaseConnection();
                }
            }
    public void releaseConnection(HttpConnection conn) {
            if (conn != httpConnection) {
                throw new IllegalStateException("Unexpected release of an unknown connection.");
            }
    
            finishLastResponse(httpConnection);
            
            inUse = false;
    
            // track the time the connection was made idle
            idleStartTime = System.currentTimeMillis();
        }

     这个地方我们可以看到了所谓的释放连接并不是真的释放,还是return the connection to pool,照此分析,我们每个线程中new了一个HttpClient类,而每个HttpClient类中的链接都是没有close的,只是归还到 httpClient中的pool而已,这些连接也必须等到连接超时才会被释放,由此可以分析出来连接数上涨的原因。那么我们应该怎么使用呢?按照代码的 设计,看起来httpclient应该是单例的,但是在httpClient类的javadoc中并没有关于线程安全方面的说明,为此我们再回到官网上看 相关文档,在文档(http://hc.apache.org/httpclient-3.x/performance.html)上我们看到如下的说 明:

    HttpClient is fully thread-safe when used with a thread-safe connection manager such as MultiThreadedHttpConnectionManager

     这说明在多线程环境下应该使用一个全局单例的HttpClient,并且使用MultiThreadHttpConnectionManager来管理Connection。

    【相关结论】

    1、HttpClient内部使用了池化技术,内部的链接是为了复用。在多线程条件下,可以使用一个全局的HttpClient实例,并且使用MultiThreadHttpConnectionManager来管理Connection。

    2、使用开源软件之前一定要读读相关代码,看看官方推荐使用方式。

    3、在解决此问题后,读了读httpclient中其他包中的代码,在读的时候发现对于理解http协议帮助很大,特别是文件上传,长连接,auth鉴权等。

  • 相关阅读:
    Sql Server 2008卸载后再次安装一直报错
    listbox 报错 Cannot have multiple items selected when the SelectionMode is Single.
    Sql Server 2008修改Sa密码
    学习正则表达式
    Sql Server 查询第30条数据到第40条记录数
    Sql Server 复制表
    Sql 常见面试题
    Sql Server 简单查询 异步服务器更新语句
    jQuery stop()用法以及案例展示
    CSS3打造不断旋转的CD封面
  • 原文地址:https://www.cnblogs.com/zhangshiwen/p/5760589.html
Copyright © 2011-2022 走看看