geek的技术日志,记录每一次技术思考的闪光点。

关于HttpClient中method.getResponseBody()的大坑:Going to buffer response body of large or unknown size. Using getResponseBodyAsStream instead is recommended

公司系统程序今天突然无法解析域名!

然而在服务器上可以ping通域名,也可以Telnet连上。

日志一个error都没爆。除了爆UnknownHostException之外.
幸亏及时启用万能的重启大法,马上就恢复正常了.
后来慢慢翻日志,后面才发现是HttpClient的坑。

发现系统在无法解析域名之前报出了“Going to buffer response body of large or unknown size. Using getResponseBodyAsStream instead is recommended.”的WARN日志.

使用万能的百度查这个错误,通通都指向: HttpClient,检查代码,会发现都是在

method.getResonseBodyAsString()或者method.getResponseBody()的地方爆出来的。
可通过如下方式解决该问题:
/////////////
BufferedReader reader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream(),”UTF-8″));
StringBuffer stringBuffer = new StringBuffer();
String str0 = “”;
while((str0 = reader.readLine())!=null){
stringBuffer.append(str0);
}
String str = stringBuffer.toString();
//获取响应的html 文本
//String str = method.getResponseBodyAsString();//原有会报警的代码。
定位到HttpClient的源码如下:
public byte[] getResponseBody() throws IOException {
    if (this.responseBody == null) {
        InputStream instream = getResponseBodyAsStream();
        if (instream != null) {
            long contentLength = getResponseContentLength();
            if (contentLength > Integer.MAX_VALUE) { //guard below cast from overflow
                throw new IOException("Content too large to be buffered: "+ contentLength +" bytes");
            }
            int limit = getParams().getIntParameter(HttpMethodParams.BUFFER_WARN_TRIGGER_LIMIT, 1024*1024);
            if ((contentLength == -1) || (contentLength > limit)) {
                LOG.warn("Going to buffer response body of large or unknown size. "
                        +"Using getResponseBodyAsStream instead is recommended.");
            }
            LOG.debug("Buffering response body");
            ByteArrayOutputStream outstream = new ByteArrayOutputStream(
                    contentLength > 0 ? (int) contentLength : DEFAULT_INITIAL_BUFFER_SIZE);
            byte[] buffer = new byte[4096];
            int len;
            while ((len = instream.read(buffer)) > 0) {
                outstream.write(buffer, 0, len);
            }
            outstream.close();
            setResponseStream(null);
            this.responseBody = outstream.toByteArray();
        }
    }
    return this.responseBody;
}
报WARN的条件是((contentLength == -1) || (contentLength > limit)),也就是说,或者是返回的HTTP头没有指定contentLength,或者是contentLength大于上限(默认是1M)。如果能确定返回结果的大小对程序没有显著影响,这个WARN就可以忽略,可以在日志的配置中把HttpClient的日志级别调到ERROR,不让它报出来。
 当然,这个警告也是有意义的,HttpClient建议使用InputStream getResponseBodyAsStream()代替byte[] getResponseBody()。对于返回结果很大或无法预知的情况,就需要使用InputStream getResponseBodyAsStream(),避免byte[] getResponseBody()可能带来的内存的耗尽问题(没设置值时,默认1024*1024=1M,每次就花掉1M,1G内存也就只够请求1024次)。
而BufferReader的默认大小是:默认缓冲区大小为8192字节,即8KB。8k和1M对内存的消耗差别是很大的,差一百多倍。
内存耗尽会带来各种问题,我遇到的情况是,内存耗尽虽然表面上系统还在正常运行,日志也正常打印,但由此导致httpClient的相关线程被关闭,域名直接无法解析,所有使用到需要解析域名的功能都无法正常使用(包括上传图片到oss,连接第三方api服务器等等)。

未经允许不得转载:极客技术 » 关于HttpClient中method.getResponseBody()的大坑:Going to buffer response body of large or unknown size. Using getResponseBodyAsStream instead is recommended

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址