Java 使用EntityUtils.consume(httpEntity)直到流结束的消费如何导致将连接释放回连接池?
我读到Java 使用EntityUtils.consume(httpEntity)直到流结束的消费如何导致将连接释放回连接池?,java,connection-pooling,httpconnection,elasticsearch-rest-client,Java,Connection Pooling,Httpconnection,Elasticsearch Rest Client,我读到EntityUtils.consume(httpEntity)将导致将连接释放回连接池,但当我查看源代码时,我无法理解这是如何发生的。有人能告诉我代码的哪一部分吗?EntityUtils.consume(httpEntity)或EntityUtils.toString(httpEntity)在我使用弹性搜索Rest客户端(org.elasticsearch.Client.RestClient-低级Rest客户端)时释放连接,如下: 如果存在SocketTimeoutException且我不
EntityUtils.consume(httpEntity)
将导致将连接释放回连接池,但当我查看源代码时,我无法理解这是如何发生的。有人能告诉我代码的哪一部分吗?EntityUtils.consume(httpEntity)
或EntityUtils.toString(httpEntity)
在我使用弹性搜索Rest客户端(org.elasticsearch.Client.RestClient-低级Rest客户端)时释放连接,如下:
如果存在SocketTimeoutException且我不使用HttpEntity,连接会发生什么情况?客户端关闭并释放到池的连接(步骤)
EntityUtils.consume
&EntityUtils.toString
>第一个将关闭()。第二个将始终在其finally子句中调用instream.close()
instream
是为InputStream变量指定的名称
instream
close()
>在本例中,InputStream
的实现是一个简单的过程。close()
方法通过代码片段中显示的循环机制强制读取ContentInputStream
,直到其结束
以下对此流的调用将导致异常
@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();
}
池
>检查所有池资源状态。此操作可能由某些操作触发(作为新请求),也可能由底层线程管理。如果一个资源/流被另一端关闭,它将获得一个EOF
异常(因为缓冲区被迫前进到一端)。该点被标记为无效
池
>所有无效点均回收。它将删除已关闭的流并创建新的流,或恢复现有流,而无需擦除+创建(取决于资源类型)。这意味着保存流的点可以再次使用,新流可以使用:
连接被释放回池。另一端不再使用它,因此池可以完全控制它。现在,允许池擦除、恢复并将其分配给其他请求者。
示例 让我们设想一个
池
管理3种资源,例如HttpConnections
。
你已经有3个线程在使用这个池,所以所有的点都被占用了
同时ThreadZ
等待将连接释放回池
ThreadA
已完成其作业并关闭其连接。当池条目的状态为关闭时,池将注意到这一点。不同的PoolEntry
实现将以不同的方式检查这一点,其中一种方式是在尝试从流读取时获得EOF
异常。其他实现可能有不同的机制来检查资源是否关闭。如果PoolEntry
告知其资源已关闭/无效
,池将回收此点。这里有两个选项:
a) 擦除并创建
b) 恢复
“重新释放连接”可以翻译为“现在又有了可用的地点/资源”。池现在可以连接到ThreadZ
:
(spot1) [HttpConn1] -- ThreadZ
(spot2) [HttpConn2] -- ThreadB
(spot3) [HttpConn3] -- ThreadC
消费
/toString
-连接释放
上述所有说明意味着在
输入流中调用close()
将触发连接释放
这发生在消费
(如果实体内容已完全消费)和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
,特定的PoolEntry
实现也可以检查资源是否无效,而无需调用close()
。使用close()
@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();
}
但是特定的池
实现还可以检查资源是否无效,即使未被修补的异常
不允许您专门调用close()
,因为它们可以使用不同的机制检查状态。例如,检查连接处于空闲状态的时间。如果此时间优于由池标记的某个treshold,则此点将被回收,而无需客户端先前的close()
调用
这一次,池
将是调用它的close()
的终端,避免了客户端可能出现的死锁
,如果这个池不能管理最大连接时间或某些异常。谢谢,不过我应该在我的问题中更具体一些。我查看了instream.close(),如果我们以ContentInputStream或ByteArrayInputStream为例,close()代码的哪一部分触发了连接释放?InputStream.close()似乎什么也没做。释放的基本定义是基于池的性质。关闭意味着摧毁资源,因此现在只有一个点o
(spot1) [HttpConn1] -- ThreadZ
(spot2) [HttpConn2] -- ThreadB
(spot3) [HttpConn3] -- ThreadC
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
}
}
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. */
}
}
}