简体   繁体   English

使用 EntityUtils.consume(httpEntity) 直到 stream 结束时消费如何导致将连接释放回连接池?

[英]How does consuming till the end of stream using EntityUtils.consume(httpEntity) result in releasing the connection back to the connection pool?

I read here that EntityUtils.consume(httpEntity) will result in releasing the connection back to the connection pool, but when I looked at the source code, I couldn't understand how is that happening.在这里EntityUtils.consume(httpEntity)将导致将连接释放回连接池,但是当我查看源代码时,我无法理解这是怎么回事。 Can someone please point me to the part of the code where EntityUtils.consume(httpEntity) or EntityUtils.toString(httpEntity) is releasing the connection when I'm using Elastic Search Rest Client (org.elasticsearch.client.RestClient - low level rest client) like this: https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/java-rest-low-usage-responses.html ? Can someone please point me to the part of the code where EntityUtils.consume(httpEntity) or EntityUtils.toString(httpEntity) is releasing the connection when I'm using Elastic Search Rest Client (org.elasticsearch.client.RestClient - low level rest客户端)像这样: https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/java-rest-low-usage-responses.html

What happens to the connection if there is a SocketTimeoutException and I don't consume the HttpEntity?如果存在 SocketTimeoutException 并且我不使用 HttpEntity,连接会发生什么情况?

Client-side close and connection release to the Pool (steps)客户端关闭和连接释放到池(步骤)

  1. EntityUtils.consume & EntityUtils.toString > the first one will close() the instream if it fully consumes the entity. EntityUtils.consume & EntityUtils.toString > 如果完全消耗实体,第一个将close() instream The second one will always call instream.close() in its finally clause.第二个总是在它的finally子句中调用instream.close() instream is the name given to the InputStream variable. instream是 InputStream 变量的名称。

  2. instream . instream close() > For this example, the implementation of the InputStream is a ContentInputStream . close() > 对于这个例子, InputStream的实现是一个ContentInputStream The close() method forces the ContentInputStream to be read till its end by the loop mechanism shown in the code snippet. close()方法通过代码片段中显示的循环机制强制读取ContentInputStream直到其结束。

    Following calls to this stream will lead to an EOF exception.调用此 stream 将导致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(); }
  3. Pool > Checks all pooled resources status. Pool > 检查所有池资源状态。 This operation may be triggered by some actions (as a new request), or may be managed by underlying threads.此操作可能由某些操作(作为新请求)触发,也可能由底层线程管理。 If one resource/stream was closed by the other end, it will get an EOF exception ( as the buffer was forced to advance to the end ).如果一个资源/流被另一端关闭,它将得到一个EOF异常(因为缓冲区被迫前进到最后)。 The spot is marked as invalid.该地点被标记为无效。

  4. Pool > All invalid spots are recycled . Pool >所有无效点被回收 It will remove the closed streams and create new ones, or restore the existing ones without the need of erase+create (depending on the resource type).它将删除关闭的流并创建新的流,或者在不需要擦除+创建的情况下恢复现有流(取决于资源类型)。 This means the spot that was holding the stream is avaliable again, with a new stream ready to be used:这意味着持有 stream 的位置再次可用,新的 stream 可以使用:

    The connection is released back to the pool.连接被释放回池。 The other end is not using it anymore, so the pool has total control of it.另一端不再使用它,因此池可以完全控制它。 Now the pool is allowed to erase it, restore it, and assign it to another requester.现在允许池删除、恢复它并将其分配给另一个请求者。 . .


Example例子

Let's imagine a Pool that manages 3 resources, such as HttpConnections .让我们想象一个管理 3 个资源的Pool ,例如HttpConnections You already have 3 threads using this pool, so the spots are all occupied.你已经有 3 个线程在使用这个池,所以这些点都被占用了。

Meanwhile ThreadZ waits for a connection to be released back to the Pool同时ThreadZ等待连接被释放回池

 (spot1) [HttpConn1] -- ThreadA
 (spot2) [HttpConn2] -- ThreadB
 (spot3) [HttpConn3] -- ThreadC

ThreadA finished its job and closes its connection. ThreadA完成了它的工作并关闭了它的连接。 The Pool will notice this when the status of the PoolEntry is closed .PoolEntry的状态为closed时, Pool会注意到这一点。 Different PoolEntry implementations will check this is different ways, one of them being getting an EOF exception while trying to read from a stream.不同PoolEntry实现会以不同的方式进行检查,其中之一是在尝试从 stream 读取时遇到EOF异常。 Other implementations could have different mechanisms to check if the resource is closed .其他实现可能有不同的机制来检查资源是否已关闭 If the PoolEntry tells that his resource is closed/invalid , the Pool will recycle this spot.如果PoolEntry告知他的资源已关闭/无效,则Pool将回收该点。 There are two options here:这里有两个选项:

a) Erase and create. a) 擦除和创建。

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

b) Restore. b) 恢复。

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

"Releasing the connection back" could be translated to "now there's an avaliable spot/resource again". “释放连接”可以翻译为“现在又有一个可用的点/资源”。 The pool can now give a connection to ThreadZ :池现在可以提供与ThreadZ的连接:

 (spot1) [HttpConn1] -- ThreadZ
 (spot2) [HttpConn2] -- ThreadB
 (spot3) [HttpConn3] -- ThreadC

consume / toString - connection release consume / toString - 连接释放

All explained above means that calling close() in the InputStream will trigger the connection release .上面所有的解释意味着InputStream调用close()将触发连接释放

This happens both in consume ( if the entity content is fully consumed ) and toString methods:这发生在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
    } 
} 

What happens to the connection if there is a SocketTimeoutException and I don't consume the HttpEntity?如果存在 SocketTimeoutException 并且我不使用 HttpEntity,连接会发生什么情况?

As you notice, both methods throw an IOException , and SocketTimeoutException is inherited from it.正如您所注意到的,这两种方法都抛出一个IOException ,并且SocketTimeoutException是从它继承而来的。 It is responsability of the caller to catch this exception and manage to close all resources if such scenario happens.如果发生这种情况,调用者有责任捕获此异常并设法关闭所有资源。 For example:例如:

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. */
      }
   }
}

Notice that if a SocketTimeoutException is thrown, specific PoolEntry implementations could also check if the resource is invalid without the need of a close() call.请注意,如果抛出SocketTimeoutException ,特定PoolEntry实现还可以检查资源是否无效,而无需调用close() Using close() guarantees that the Pool will recycle the spot after a correct use of it, and can be used as well as a "invalid marker" when you are able to catch a thrown exception.使用close()可以保证Pool在正确使用后回收该点,并且当您能够捕获抛出的异常时,可以将其用作“无效标记”。

But specific Pool implementations will be able to also check if a resource is invalid even if an uncatched Exception didn't let you specifically call close() , as they would be able to check the status with different mechanisms.但是即使未捕获的Exception不允许您专门调用close() ,特定的Pool实现也将能够检查资源是否无效,因为它们将能够使用不同的机制检查状态。 For example, checking how many time a connection was in IDLE state .例如,检查连接在IDLE state 中的时间 If this time is superior to a certain treshold marked by the Pool , this spot will be recycled without the need of a previous close() call from the client.如果此时间优于Pool标记的某个阈值,则该点将被回收,而无需客户端先前的close()调用。

This time the Pool will be the end that calls close() on it, avoiding a possible deadlock in the client side, if this one doesn't manage the max connection time or certain exceptions.这一次Pool将是调用close()的结束,如果这个不管理最大连接时间或某些异常,则避免客户端可能出现deadlock

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM