HttpClient在成功执行方法后卡在socketRead0上

8

我们的Web应用程序中,用户可以提交一个URL。我们将在服务器端获取并解析数据。对于每个请求,我们使用一个带有以下(相关)设置的HttpClient:

connectionManager.getParams().setConnectionTimeout(10000);
connectionManager.getParams().setSoTimeout(10000);

当我调用HttpMethod.getResponseBody时,状态码已经被检查为可接受。此时线程会挂起,并显示以下堆栈跟踪:
java.net.SocketInputStream.socketRead0 ( native code )
java.net.SocketInputStream.read ( SocketInputStream.java:150 )
java.net.SocketInputStream.read ( SocketInputStream.java:121 )
java.io.BufferedInputStream.read1 ( BufferedInputStream.java:273 )
java.io.BufferedInputStream.read ( BufferedInputStream.java:334 )
java.io.FilterInputStream.read ( FilterInputStream.java:133 )
org.apache.commons.httpclient.AutoCloseInputStream.read ( AutoCloseInputStream.java:108 )
java.io.FilterInputStream.read ( FilterInputStream.java:107 )
org.apache.commons.httpclient.AutoCloseInputStream.read ( AutoCloseInputStream.java:127 )
org.apache.commons.httpclient.HttpMethodBase.getResponseBody ( HttpMethodBase.java:690 )

我无法确定发生这种情况的确切URL(这是在现场环境中发生的事件),并且我无法重现它。我想这可能只是我们连接到的服务器行为异常的问题,但也许我漏掉了什么。在任何情况下,有没有办法防止阻塞方法调用永远等待?SoTimeout也是套接字读取超时?还有其他设置我错过了吗?

5个回答

4

我已经正确地设置了所有的超时时间,但我发现我们有一个url使用http分块(chunking)但不发送结果(在chrome中正常工作,但在http客户端中即使设置了超时也会永远卡住)。幸运的是,我拥有该服务器并返回一些垃圾数据,它就不再卡住了。这似乎是一个非常独特的错误,因为http客户端无法处理某种空的分块情况(虽然我可能完全错了)... 我只知道每次都会在那个相同的URL中挂起,并且该URL是用于将CSV文件通过http分块下载回我们的http客户端。


@Dean Hiller:这没有任何意义。套接字超时适用于套接字读取操作,与任何HTTP协议元素无关。如果您仍然可以重现此问题,请在HC项目中提出JIRA,并提交显示问题的会话的线路日志。 - ok2c
我同意你的观点,在我的经验中,超时通常是有效的...可能是我搞砸了其他什么东西,但返回数据解决了这个问题。 - Dean Hiller

1

HttpClient区分连接和请求。 setSoTimeout将配置连接套接字超时,而setConnectionTimeout将同时配置连接管理器的超时(等待连接的时间)和连接本身的建立超时。在您提供的代码中,您没有为用于请求本身的套接字设置任何超时,不幸的是,HttpClient默认情况下没有超时。

以下是我在v4.4.1中的做法:

// Configure the socket timeout for the connection, incl. ssl tunneling
connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(200);
connManager.setDefaultMaxPerRoute(100);

SocketConfig sc = SocketConfig.custom()
    .setSoTimeout(soTimeoutMs)
    .build();

connManager.setDefaultSocketConfig(sc);

HttpClient client = HttpClients.custom()
            .setConnectionManager(connManager)
            .setConnectionManagerShared(true)
            .build();

// configure the timeouts (socket and connection) for the request
RequestConfig.Builder config = = RequestConfig.copy(RequestConfig.DEFAULT);
config.setConnectionRequestTimeout(connectionTimeoutMs);
config.setSocketTimeout(socketTimeoutMs);

HttpRequestBase req = new HttpGet(uri);
req.setConfig(config.build());

client.execute(req);

setConnectTimeout != setConnectionRequestTimeout and there is no setConnectionTimeout - Antoniossss

0
当我调用HttpMethod.getResponseBody时,状态码已经被检查为可接受。此时线程挂起。
看起来你在同步调用方面有问题...你应该确保该方法...

HttpMethod.getResponseBody

在更改状态码的部分,应该按顺序调用或使用互斥锁(信号量)。

此外,您应该减少超时限制以防止挂起。


检查方法是否成功和获取响应在同一线程上进行。不存在并发问题。HttpClient获取并更新状态。调用挂起无限期,所述超时不被尊重,更改它没有效果。 - Kafkaesque
我的问题更糟,而且在多线程应用程序中每次都会发生,尽管需要大约800个调用才会发生,因此调试起来非常麻烦...顺便说一句,它会在通常返回数据块的调用上挂起(http分块),但是这个调用是没有数据的,由于某种原因http客户端挂起...可能是服务器出了问题,但我在chrome中手动尝试了该url,它可以正常工作...只有http客户端遇到了困难。 - Dean Hiller
我发布了我们的堆栈跟踪,它非常接近。 - Dean Hiller

0

0
我们在实现中经常遇到这个问题,看起来HTTP客户端没有正确处理错误的服务器或者出现了某些问题,而且它没有超时...我可以在我们的数据总线开源项目中复现这个问题,而且堆栈跟踪稍有不同...

SocketInputStream.socketRead0(FileDescriptor, byte[], int, int, int) 行:不可用 [本机方法]
SocketInputStream.read(byte[], int, int) 行:129
SocketInputBuffer(AbstractSessionInputBuffer).fillBuffer() 行:166
SocketInputBuffer.fillBuffer() 行:90 SocketInputBuffer(AbstractSessionInputBuffer).readLine(CharArrayBuffer) 行:281
DefaultHttpResponseParser.parseHead(SessionInputBuffer) 行:92
DefaultHttpResponseParser.parseHead(SessionInputBuffer) 行:62
DefaultHttpResponseParser(AbstractMessageParser).parse() 行:254
DefaultClientConnection(AbstractHttpClientConnection).receiveResponseHeader() 行:289 DefaultClientConnection.receiveResponseHeader() 行:252
BasicPooledConnAdapter(AbstractClientConnAdapter).receiveResponseHeader() 行:219 HttpRequestExecutor.doReceiveResponse(HttpRequest, HttpClientConnection, HttpContext) 行:300 HttpRequestExecutor.execute(HttpRequest, HttpClientConnection, HttpContext) 行:127
DefaultRequestDirector.tryExecute(RoutedRequest, HttpContext) 行:712 DefaultRequestDirector.execute(HttpHost, HttpRequest, HttpContext) 行:517
DefaultHttpClient(AbstractHttpClient).execute(HttpHost, HttpRequest, HttpContext) 行:906 DefaultHttpClient(AbstractHttpClient).execute(HttpUriRequest, HttpContext) 行:805
ReadAggregations.processAggregation(String, Counter, Counter, Counter, Counter) 行:153
ReadAggregations.start() 行:96
ReadAggregations.main(String[]) 行:70


我今天仍然面临这个问题。你知道有没有替代apache httpclient的工具,它不会在与坏服务器通信时挂起吗? - Arya
@Arya 我最终编写了一个http1.1客户端和一个http2客户端,它们在这里 https://github.com/deanhiller/webpieces (这是一整套的webpieces)。虽然这还处于beta版本,但具有独特的反压功能(由于我正在解决该错误,因此某种原因需要在客户端中关闭该功能)。另外,它看起来像一个Web服务器,但实际上Web服务器是建立在相同的http解析器、相同的nio层等基础上的,而且客户端是异步的。虽然它还处于beta版本,但是它是为测试而编写的。 - Dean Hiller
@Arya 完成这个句子,就像这篇文章中所示:https://blog.twitter.com/engineering/en_us/topics/insights/2017/the-testing-renaissance.html 注意:Web服务器比客户端进行了更多的测试(最终测试HTTP解析器),但我最近正在处理客户端。 - Dean Hiller
@Arya 哈哈,天啊,我完全忘记了,还有一个 http2to1_1-client,意味着你处理 http2 对象并通过 http1.1 协议进行通信,以便与 http2 客户端一对一交换(即 API 相同)。我忘记了我做过这个。哈哈。我一直在记录 Web 服务器,但尚未记录客户端 :( 所以我仍然需要这样做,虽然没有太多内容,因为对象与协议是一对一的,留下客户端只需处理进出的对象。 - Dean Hiller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接