Ngoại lệ sử dụng HttpRequest.execute (): Sử dụng SingleClientConnManager không hợp lệ: kết nối vẫn được cấp phát


81

Tôi đang sử dụng google-api-client-java 1.2.1-alpha để thực hiện yêu cầu POST và nhận được stacktrace sau khi tôi thực thi () HttpRequest.

Nó xảy ra ngay lập tức sau khi tôi bắt và bỏ qua lỗi 403 từ POST trước đó đến cùng một URL và sử dụng lại phương tiện truyền tải cho yêu cầu tiếp theo. (Nó trong một vòng lặp chèn nhiều mục vào cùng một nguồn cấp dữ liệu ATOM).

Có điều gì tôi nên làm để 'dọn dẹp' sau 403 không?

Exception in thread "main" java.lang.IllegalStateException: Invalid use of SingleClientConnManager: connection still allocated.
Make sure to release the connection before allocating another one.
    at org.apache.http.impl.conn.SingleClientConnManager.getConnection(SingleClientConnManager.java:199)
    at org.apache.http.impl.conn.SingleClientConnManager$1.getConnection(SingleClientConnManager.java:173)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:390)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:554)
    at com.google.api.client.apache.ApacheHttpRequest.execute(ApacheHttpRequest.java:47)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:207)
    at au.com.machaira.pss.gape.RedirectHandler.execute(RedirectHandler.java:38)
    at au.com.machaira.pss.gape.ss.model.records.TableEntry.executeModification(TableEntry.java:81)

Tại sao đoạn mã bên dưới tôi lại cố gắng có được một kết nối mới ?


Điều này vẫn có vẻ là một vấn đề với phiên bản 1.11.0-beta: /
sjngm

5
Vì lợi ích của bất kỳ ai đến đây sau khi cố gắng đọc các câu trả lời và vẫn nhận được cảnh báo - tôi đã tìm thấy câu trả lời chính xác ở đây: tech.chitgoks.com/2011/05/05/…
Steelight

@Steelight - sử dụng phương pháp tech.chitgoks.com đã giải quyết được vấn đề của tôi.
Cale Sweeney

Câu trả lời:


82

Bạn cần sử dụng phần thân phản hồi trước khi có thể sử dụng lại kết nối cho một yêu cầu khác. Bạn không chỉ nên đọc trạng thái phản hồi mà còn đọc phản hồi InputStreamđầy đủ đến byte cuối cùng, theo đó bạn chỉ cần bỏ qua các byte đã đọc.


1
Điều đó là vậy đó! Trong trường hợp của google-api-java-client, điều này có nghĩa là bắt được IOExceptionném qua HttpResponse.execute(), thử nghiệm / truyền nó tới HttpResponseException, truy cập responsethành viên và sau đó gọi paraseAsString()nó. (Mà hóa ra là thông tin hữu ích dù sao đi nữa: -)
David Bullock

5
Ngoài ra còn có một phương thức HttpEntity.consumeContent () để loại bỏ nội dung.
Grzegorz Adam Hankiewicz

3
EntityUtils.consume (entity) - ConsumerContent hiện không được dùng nữa.
David Carboni

Chỉ cần lưu ý rằng kể từ các phiên bản gần đây của Google Http Java Client (ít nhất là 1.16, nhưng có thể sớm hơn), việc gọi HttpRequest.execute()sẽ tự động dọn dẹp các tài nguyên này nếu phương thức không trả lại một HttpResponseđối tượng. Hơn nữa, JavaDoc HttpResponsehiện tại khuyên bạn nên gọiresponse.disconnect() trong trường hợp không đọc đầy đủ nội dung của phản hồi HTTP (trong một finallykhối).
David Bullock

Vấn đề tương tự với RestEasy 3.0.4 sử dụng nội bộ BasicClientConnectionManager của apache-httpclient 4.2.1. Cảm ơn bạn @BalusC. Ngoài ra, tôi chưa bao giờ đọc bất cứ điều gì về việc đọc luồng một cách đầy đủ, người ta nên xem ở đâu khi nhận thức được những cạm bẫy này? (tài liệu khôn ngoan, ngoài mã nguồn)
bảng điều khiển

42

Tôi đã gặp phải sự cố tương tự khi sử dụng HttpClient với Jetty để xây dựng khung thử nghiệm. Tôi đã phải tạo nhiều yêu cầu tới Servelet từ máy khách của mình, nhưng Nó đưa ra cùng một ngoại lệ khi thực thi.

Tôi đã tìm thấy một giải pháp thay thế tại http://foo.jasonhudgins.com/2010/03/http-connections-revisited.html

Bạn cũng có thể sử dụng phương pháp sau đây để khởi tạo khách hàng của mình.

public static DefaultHttpClient getThreadSafeClient()  {

    DefaultHttpClient client = new DefaultHttpClient();
    ClientConnectionManager mgr = client.getConnectionManager();
    HttpParams params = client.getParams();
    client = new DefaultHttpClient(new ThreadSafeClientConnManager(params, 

            mgr.getSchemeRegistry()), params);
    return client;
}

Cảm ơn, làm việc khá tốt. Tuy nhiên tôi tò mò về sự cần thiết để tạo ra hai DefaultHttpClients
htafoya

2
Anh ta sử dụng HttpParams mặc định (lấy chúng từ máy khách) thay vì tạo riêng từ đầu.
Marcin Gil

Điều đó đã giải quyết được vấn đề của tôi, nhưng liệu sử dụng nó trong ứng dụng Android có ổn không.
manish

Thật thú vị, điều đó một phần phù hợp với tôi, cuối cùng chặn đoạn mã còn lại thực thi khi tôi thực hiện một số yêu cầu HTTP trong một vòng lặp và không sử dụng các phản hồi. Và cũng không thất bại, tôi đã không đợi quá lâu để xem liệu lỗi hết thời gian có xảy ra hay không. Vì vậy, thay vào đó, tôi đã đi theo lộ trình phân bổ DefaultHttpClients mới cho mỗi yêu cầu và điều đó phù hợp với tôi, vì tôi không chạy vòng lặp lâu lắm.
David

9

Một thông báo ngoại lệ tương tự (vì ít nhất Apache Jarkata Commons HTTP Client 4.2) là:

java.lang.IllegalStateException: Invalid use of BasicClientConnManager: connection still allocated. Make sure to release the connection before allocating another one.

Ngoại lệ này có thể xảy ra khi hai hoặc nhiều luồng tương tác với một luồng org.apache.http.impl.client.DefaultHttpClient.

Làm thế nào bạn có thể tạo một DefaultHttpClientthreadsafe phiên bản 4.2 ( threadsafe theo nghĩa là hai hoặc nhiều luồng có thể tương tác với nó mà không nhận được thông báo lỗi ở trên)? Cung cấp DefaultHttpClientmột tập hợp kết nối ClientConnectionManagerdưới dạng org.apache.http.impl.conn.PoolingClientConnectionManager!

/* using
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.2.2</version>
    </dependency>
*/

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.impl.conn.SchemeRegistryFactory;
import org.apache.http.params.HttpParams;
import org.apache.http.client.methods.HttpGet;

public class MyComponent {

    private HttpClient client;

    {
        PoolingClientConnectionManager conMan = new PoolingClientConnectionManager( SchemeRegistryFactory.createDefault() );
        conMan.setMaxTotal(200);
        conMan.setDefaultMaxPerRoute(200);

        client = new DefaultHttpClient(conMan);

        //The following parameter configurations are not
        //neccessary for this example, but they show how
        //to further tweak the HttpClient
        HttpParams params = client.getParams();
        HttpConnectionParams.setConnectionTimeout(params, 20000);
        HttpConnectionParams.setSoTimeout(params, 15000);
    }


    //This method can be called concurrently by several threads
    private InputStream getResource(String uri) {
        try {
            HttpGet method = new HttpGet(uri);
            HttpResponse httpResponse = client.execute(method);
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            InputStream is = null;
            if (HttpStatus.SC_OK == statusCode) {
                logger.debug("200 OK Amazon request");
                is = httpResponse.getEntity().getContent();
            } else {
                logger.debug("Something went wrong, statusCode is {}",
                        statusCode);
                 EntityUtils.consume(httpResponse.getEntity());
            }
            return is;
        } catch (Exception e) {
            logger.error("Something went terribly wrong", e);
            throw new RuntimeException(e);
        }
    }
}

8

Đây là một câu hỏi thường được hỏi. Phản hồi của BalusC là đúng. Vui lòng bắt HttpReponseException và gọi HttpResponseException. phản hồi . bỏ qua (). Nếu bạn cần đọc thông báo lỗi, hãy sử dụng phản hồi. parseAsString () nếu bạn không biết loại nội dung phản hồi, ngược lại nếu bạn biết loại nội dung hãy sử dụng phản hồi. parseAs (MyType.class).

Một đoạn mã đơn giản từ YouTubeSample.java trong youtube-jsonc-sample (mặc dù thông thường bạn sẽ muốn làm điều gì đó thông minh hơn trong ứng dụng thực):

  } catch (HttpResponseException e) {
    System.err.println(e.response.parseAsString());
  }

Tiết lộ đầy đủ: Tôi là chủ sở hữu của dự án google-api-java-client .


15
Thực tế đây không phải là một câu hỏi thường được hỏi cho thấy rằng có một vấn đề về khả năng sử dụng trong thiết kế thư viện của bạn?
sanity

@sanity Hoặc có thể trong http chồng mình;)
krosenvold

3

Tôi đã gặp vấn đề tương tự với Responseđối tượng jax-rs (resteasy) trong các bài kiểm tra đơn vị của mình. Tôi đã giải quyết vấn đề này bằng một lệnh gọi tới response.releaseConnection(); The releaseConnection () - Phương thức chỉ có trên ClientResponseđối tượng resteasy , vì vậy tôi phải thêm một cast từ Responseđến ClientResponse.


Điều đó đã cứu ngày của tôi! Trong trường hợp của tôi, tôi phải truyền tới org.jboss.resteasy.client.jaxrs.internal.ClientResponse để thu hẹp thêm.
user2081279 22/03 '19

1

Thử đi

HttpResponse response = Client.execute(httpGet);
response.getEntity().consumeContent();
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
        //task
    Log.i("Connection", "OK");
    }else{
     Log.i("Connection", "Down");
    }

0

Ok, tôi gặp sự cố tương tự, tất cả những giải pháp đó không hoạt động, tôi đã thử nghiệm trên một số thiết bị, vấn đề là ngày trong thiết bị, đó là năm 2011 thay vì năm 2013, hãy kiểm tra điều này cũng có thể giúp đỡ.


0

Đọc InputStream như thế này:

if( response.getStatusLine().getStatusCode() == 200 ) {
    HttpEntity entity = response.getEntity();
    InputStream content = entity.getContent();
    try {
        sb = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader( new InputStreamReader( content ), 8 );
        String line;
        while( ( line = bufferedReader.readLine() ) != null ) {
            sb.append( line );
        }
        bufferedReader.close();
        content.close();
    } catch( Exception ex ) {
        Log.e( "statusCode", ex.getMessage() + "" );
    }
}

0

chỉ cần sử dụng phản hồi như bên dưới, điều đó sẽ giải quyết được vấn đề

response.getEntity().consumeContent();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.