使用entityutils.consume(httpentity)直到流结束的消费如何导致将连接释放回连接池?

shstlldc  于 2021-07-03  发布在  Java
关注(0)|答案(1)|浏览(914)

我在这里读到 EntityUtils.consume(httpEntity) 将导致将连接释放回连接池,但是当我查看源代码时,我不明白这是怎么发生的。有人能告诉我代码的哪一部分吗 EntityUtils.consume(httpEntity) 或者 EntityUtils.toString(httpEntity) 在使用elastic search rest客户端(org.elasticsearch.client.restclient-低级rest客户端)时释放连接,如下所示:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/java-rest-low-usage-responses.html?
如果存在sockettimeoutexception并且我不使用httpentity,连接会发生什么情况?

s8vozzvw

s8vozzvw1#

客户端关闭并释放到池的连接(步骤) EntityUtils.consume & EntityUtils.toString >第一个会的 close() 这个 instream 如果它完全消耗实体。第二个总会打电话来 instream.close() 在它的finally子句中。 instream 是指定给inputstream变量的名称。 instream . close() >对于本例,实现 InputStream 是一个 ContentInputStream . 这个 close() 方法强制 ContentInputStream 通过代码片段中显示的循环机制读取到其结束。
以下对此流的调用将导致 EOF 例外。

@Override
public void close() throws IOException 
{
  final byte tmp[] = new byte[1024];
  /*loop until read() is -1, which means, advance the buffer till is end*/
  while (this.buffer.read(tmp, 0, tmp.length) >= 0) {}
  super.close();
}
``` `Pool` >检查所有池资源状态。此操作可能由某些操作触发(作为新请求),也可能由底层线程管理。如果一个资源/流被另一端关闭,它将得到一个 `EOF` 异常(因为缓冲区被迫前进到末尾)。该点被标记为无效。 `Pool` >所有无效的斑点都被回收。它将删除关闭的流并创建新的流,或者恢复现有的流而不需要擦除+创建(取决于资源类型)。这意味着保存该流的点可以再次使用,并准备使用一个新流:
连接被释放回池。另一端不再使用它,所以池可以完全控制它。现在允许池擦除、还原它,并将它分配给另一个请求者。。
例子
让我们想象一个 `Pool` 它管理3个资源,例如 `HttpConnections` . 你已经有3个线程在使用这个池,所以所有的点都被占用了。
同时 `ThreadZ` 等待连接释放回池

(spot1) [HttpConn1] -- ThreadA
(spot2) [HttpConn2] -- ThreadB
(spot3) [HttpConn3] -- ThreadC
``` ThreadA 完成了它的工作并关闭了它的连接。这个 Pool 会注意到这一点时的状态 PoolEntry 已关闭。不同的 PoolEntry 实现将检查这是不同的方式,其中之一是获得 EOF 尝试从流读取时出现异常。其他实现可以有不同的机制来检查资源是否关闭。如果 PoolEntry 告知他的资源已关闭/无效,则 Pool 会回收这个地方。这里有两个选项:
a) 擦除并创建。

(spot1) [HttpConn4] // the pool removes the old one and creates a new connection
 (spot2) [HttpConn2] -- ThreadB
 (spot3) [HttpConn3] -- ThreadC

b) 恢复。

(spot1) [HttpConn1] // the pool is able to "reset" the existing resource
 (spot2) [HttpConn2] -- ThreadB
 (spot3) [HttpConn3] -- ThreadC

“释放连接回来”可以翻译成“现在又有了一个可用的地点/资源”。池现在可以连接到 ThreadZ :

(spot1) [HttpConn1] -- ThreadZ
 (spot2) [HttpConn2] -- ThreadB
 (spot3) [HttpConn3] -- ThreadC
``` `consume` / `toString` -连接释放
上面所说的一切都意味着 `close()` 在 `InputStream` 将触发连接释放。
这两种情况都会发生 `consume` (如果实体内容已完全使用)和 `toString` 方法:

public static void consume(final HttpEntity entity) throws IOException
{
if (entity == null)
return;

if (entity.isStreaming()) 
{ 
    InputStream instream = entity.getContent(); 
    if (instream != null) 
        instream.close();   // <-- connection release
} 

}

public static String toString(final HttpEntity entity, final Charset defaultCharset)
throws IOException, ParseException
{
Args.notNull(entity, "Entity");
InputStream instream = entity.getContent();
if (instream == null) {
return null;
}
try {
Args.check(entity.getContentLength() <= Integer.MAX_VALUE,
"HTTP entity too large to be buffered in memory");
int i = (int)entity.getContentLength();
if (i < 0) {
i = 4096;
}
Charset charset = null;
try {
ContentType contentType = ContentType.getOrDefault(entity);
charset = contentType.getCharset();
} catch (UnsupportedCharsetException ex) {
throw new UnsupportedEncodingException(ex.getMessage());
}
if (charset == null) {
charset = defaultCharset;
}
if (charset == null) {
charset = HTTP.DEF_CONTENT_CHARSET;
}
Reader reader = new InputStreamReader(instream, charset);
CharArrayBuffer buffer = new CharArrayBuffer(i);
char[] tmp = new char[1024];
int l;
while((l = reader.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
return buffer.toString();
} finally {
instream.close(); // <--- connection release
}
}

如果存在sockettimeoutexception并且我不使用httpentity,连接会发生什么情况?
正如您所注意到的,这两种方法都会抛出 `IOException` ,和 `SocketTimeoutException` 是从它那里继承来的。调用方负责捕获此异常,并在发生这种情况时设法关闭所有资源。例如:

void tryConsume()
{
try
{
//...
EntityUtils.consume(httpEntity);
//...
}
catch (IOException)
{
//SocketTimeoutException happened. Log the error,etc
// (Close resources here...)
}
finally
{
//...Or maybe include a finally clause and close them here, if you wish
// them to be closed regardless of success/failure.
if (httpEntity!=null)
{
InputStream instream = httpEntity.getContent();
if (instream != null)
instream.close(); /* <-- connection release. when checking this
spot, the pool will get (f.e) an EOF
exception. This will lead to replacing this
resource with a fresh new connection and
setting the spot status as avaliable. */
}
}
}

注意,如果 `SocketTimeoutException` 被抛出,具体 `PoolEntry` 实现还可以检查资源是否无效,而无需 `close()` 打电话。使用 `close()` 保证 `Pool` 将在正确使用该点后回收该点,并且可以在捕获抛出的异常时用作“无效标记”。
但是具体的 `Pool` 实现还能够检查资源是否无效,即使未调度的 `Exception` 没有让你特别打电话 `close()` ,因为他们可以用不同的机制检查状态。例如,检查一个连接的插入时间 `IDLE` 州。如果这个时间优于某一个用 `Pool` ,此点将被回收,而无需以前的 `close()` 客户来电。
这一次 `Pool` 将是召唤的终点 `close()` 在它上面,避免一个可能的 `deadlock` 在客户端,如果这个没有管理最大连接时间或某些异常。

相关问题