• 【Bug】httpClient使用代理后线程挂起


    背景:

      需要去监控某个网站,所以写了一个爬虫程序,被爬取的链接是Https,使得的是网上的代理,按ip使用量计费,该计费模式确实好用!

      框架:httpClient 4.5.10

      Java: Java 9

    implementation 'org.apache.httpcomponents:httpclient:4.5.10'

    问题:

      然后问题出现了,因为是一个监控程序,所以需要不断的轮询,然后开了10个左右线程轮询,结果跑了半小时后,10个线程全部刮起,thread dump一下发现每个线程都如下:

    "http-nio-8081-exec-7@5795" daemon prio=5 tid=0x2a nid=NA runnable
      java.lang.Thread.State: RUNNABLE
          at java.net.SocketInputStream.socketRead0(SocketInputStream.java:-1)
          at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
          at java.net.SocketInputStream.read(SocketInputStream.java:171)
          at java.net.SocketInputStream.read(SocketInputStream.java:141)
          at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
          at sun.security.ssl.InputRecord.read(InputRecord.java:503)
          at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:975)
          - locked <0x1c8d> (a java.lang.Object)
          at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:933)
          at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
          - locked <0x1c8e> (a sun.security.ssl.AppInputStream)
          at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)
          at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153)
          at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:280)
          at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)
          at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)

      WTF?一直卡在 java.net.SocketInputStream.socketRead0 方法上,难道是我没有设置超时逻辑?于是赶紧去查找代码,加上逻辑:

    HttpClient httpClient = HttpClientBuilder.create()
                                                 .setConnectionManager(connManager)
                                                 .setConnectionTimeToLive(20000L, TimeUnit.MILLISECONDS)
                                                 .setRetryHandler(getRetryHandler())
                                                 .setDefaultCredentialsProvider(getProxyProvider())
                                                 .build();

      

      自信的跑了一段时间后,无用还是挂着,于是上网看看,发现很多人都遇到过这种事,但没人说明是为啥?有几种可能:

    1. httpClient某个版本的bug,在某个版本后修复了,然后检查发现,我的版本是最新的,没问题
    2. 某博主也是用的代理,情况和我一摸一样,然后搞不出问题,所以就绕过去了,kill thread ,这真是神操作

    分析:

      现在卡在Runnable上面,而且是“阻塞”,当然不是获取锁的block状态,而是同步等待的意思;这个就比较有意思了,那么此刻线程在干嘛呢?我们分析下SocketInputStream.socketRead0这个方法到底在干嘛!

    解决:

      因为是用的是框架,对框架也不熟,所以直觉告诉我需要先考虑是框架的问题,所以开始对httpClient的使用开始研究,太恶心了,这个框架每个版本的使用方法都不太一样;最终我发现两种设置超时的方法,一个是掌控应用层的RequestConfig,第二种是掌控Socket的SocketConfig,这个时候我已经猜到了问题原因,这次信心慢慢,于是我加入了以下代码,发现问题神奇的解决了。

    BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager();
        connManager.setSocketConfig(SocketConfig.custom().setSoTimeout(2000).build());
        HttpClient httpClient = HttpClientBuilder.create()
                                                 .setConnectionManager(connManager)
                                                 .setConnectionTimeToLive(20000L, TimeUnit.MILLISECONDS)
                                                 .setRetryHandler(getRetryHandler())
                                                 .setDefaultCredentialsProvider(getProxyProvider())
                                                 .build();

     

  • 相关阅读:
    How Default Heap Of Process Grows
    希腊字母表
    Ubuntu第一次亲密接触
    Ubuntu中的挂载点(mount point)
    要一专多能
    First touch with JIT debugging
    小学一下环境变量
    安装VMware Tools
    [转]ReiserFS与ext3的比较
    [bbk4485]第二章Flashback Database 05
  • 原文地址:https://www.cnblogs.com/iCanhua/p/12076636.html
Copyright © 2020-2023  润新知