HttpRequest.execute () kullanımında özel durum: Geçersiz SingleClientConnManager kullanımı: bağlantı hala ayrılmış


81

Bir POST isteğini yürütmek için google-api-client-java 1.2.1-alpha kullanıyorum ve HttpRequest'i çalıştırdığımda () aşağıdaki yığın izini alıyorum.

Önceki bir POST'tan aynı URL'ye 403 hatasını yakalayıp yok saymamdan ve sonraki istek için aktarımı yeniden kullandıktan hemen sonra oluyor. (Aynı ATOM beslemesine birden çok giriş ekleyen bir döngü içindedir).

403'ten sonra 'temizlemek' için yapmam gereken bir şey var mı?

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)

Altımdaki kod neden yeni bir bağlantı kurmaya çalışıyor ?


Bu hala 1.11.0-beta sürümüyle ilgili bir sorun gibi görünüyor: /
sjngm

5
Yanıtları tüketmeye çalıştıktan ve hala uyarıları aldıktan sonra buraya gelen herkesin yararına - doğru cevabı burada buldum: tech.chitgoks.com/2011/05/05/…
Steelight

@Steelight - tech.chitgoks.com yaklaşımını kullanmak sorunumu çözdü.
Cale Sweeney

Yanıtlar:


82

Bağlantıyı başka bir istek için yeniden kullanmadan önce yanıt gövdesini tüketmeniz gerekir. Yalnızca yanıt durumunu okumamalı, yanıtı InputStreamson bayta kadar okumalı ve böylece okunan baytları yok saymalısınız.


1
İşte buydu! Durumunda google-api-java-client, bu alıcı anlamına IOExceptiontarafından atılan HttpResponse.execute()/ test için döküm, HttpResponseException, erişen responseyürütmesini sonra üyesini ve paraseAsString()üzerinde. (Her halükarda faydalı bilgiler olduğu ortaya çıktı: -)
David Bullock

5
İçeriği atmak için bir HttpEntity.consumeContent () yöntemi de vardır.
Grzegorz Adam Hankiewicz

3
EntityUtils.consume (varlık) - consumeContent artık kullanımdan kaldırılmıştır.
David Carboni

Google Http Java İstemcisinin son sürümlerinden itibaren (en az 1.16, ancak muhtemelen daha eski), HttpRequest.execute()yöntemin bir HttpResponsenesne döndürememesi durumunda çağrının bu kaynakları otomatik olarak temizleyeceğini unutmayın . Ayrıca, JavaDoc , HTTP yanıtının içeriğini tam olarak okumama durumunda (bir blokta) HttpResponsearamayı önermektedir . response.disconnect()finally
David Bullock

Apache-httpclient 4.2.1'in BasicClientConnectionManager'ını dahili olarak kullanan RestEasy 3.0.4 ile aynı sorun. Teşekkür ederim @BalusC. Ayrıca akışı tam olarak okumakla ilgili hiçbir şey okumam, bu tuzakların farkına varmak için nereye bakmalıyım? (dokümantasyon açısından, kaynak kodu dışında)
alegria

42

Bir test çerçevesi oluşturmak için Jetty ile HttpClient kullanırken benzer bir sorunla karşılaşıyordum. Müşterimden Servelet'e birden fazla istek oluşturmak zorunda kaldım, ancak yürütüldüğünde aynı istisnayı veriyordu.

Http://foo.jasonhudgins.com/2010/03/http-connections-revisited.html adresinde bir alternatif buldum

İstemcinizi somutlaştırmak için bu aşağıdaki yöntemi de kullanabilirsiniz.

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;
}

Teşekkürler, oldukça iyi çalıştı. Bununla birlikte, iki DefaultHttpClients oluşturma gerekliliğini merak ediyorum
htafoya

2
Sıfırdan kendi oluşturmak yerine varsayılan HttpParams kullanır (bunları istemciden alır).
Marcin Gil

Bu benim sorunumu çözdü, ancak bunu android uygulamasında kullanmak sorun değil.
manish

İlginç bir şekilde, bu kısmen benim için çalışıyor, bir döngüde birkaç HTTP isteği yaptığımda ve yanıtları tüketmediğimde kalan kodun yürütülmesini engelliyor. Ve başarısızlık da yok, yine de bir zaman aşımı hatası olup olmadığını görmek için çok uzun süre beklemedim. Bunun yerine, her istek için yeni DefaultHttpClients tahsis etme yoluna gittim ve bu döngüyü çok uzun süre çalıştırmadığım için benim için çalıştı.
David

9

Benzer bir istisna mesajı (en azından Apache Jarkata Commons HTTP Client 4.2 olduğundan):

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

Bu istisna, iki veya daha fazla iş parçacığı tek bir org.apache.http.impl.client.DefaultHttpClient.

Bir 4.2 DefaultHttpClientörneğini iş parçacığı güvenli hale nasıl getirebilirsiniz ( iki veya daha fazla iş parçacığının hata mesajının üstüne çıkmadan onunla etkileşime girebilmesi anlamında iş parçacığı güvenli)? Sağlayın DefaultHttpClientbağlantı-göllenmesi ClientConnectionManagerşeklinde 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

Bu sık sorulan bir sorudur. BalusC'un yanıtı doğru. Lütfen HttpReponseException'ı yakalayın ve HttpResponseException'ı çağırın. yanıt . yoksay (). Hata mesajını okumanız gerekiyorsa yanıtı kullanın. parseAsString () yanıt içerik türünü bilmiyorsanız, aksi takdirde içerik türünü biliyorsanız yanıtı kullanın. parseAs (MyType.class).

Gelecek basit kod parçacığı YouTubeSample.java içinde youtube-jsonc-numunenin (genellikle olsa gerçek uygulamada akıllı bir şey yapmak istersiniz):

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

Tam açıklama: google-api-java-client projesinin sahibiyim .


15
Bunun sık sorulan bir soru olması, kütüphane tasarımınızda kullanılabilirlik sorunu olduğunu göstermiyor mu?
akıl sağlığı

@sanity Veya belki http yığınının kendisinde;)
krosenvold

3

ResponseBirim testlerimde jax-rs (resteasy) nesnesiyle aynı sorunu yaşadım . Bunu response.releaseConnection(); The releaseConnection () ' a bir çağrı ile çözdüm - Metot sadece resteasy ClientResponsenesnesinde, bu yüzden den' Responsee bir cast eklemem gerekiyordu ClientResponse.


Bu benim günümü kurtardı! Benim durumumda, onu daha da daraltmak için org.jboss.resteasy.client.jaxrs.internal.ClientResponse'a döküm yapmak zorunda kaldım.
user2081279

1

Bunu dene

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

Tamam, benzer bir sorunum var, tüm bu çözümler işe yaramıyor, bazı cihazlarda test ettim, sorun cihazda tarih oldu, 2013 yerine 2011 idi, ayrıca kontrol et bu yardımcı olabilir.


0

InputStream'i şu şekilde okuyun:

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

Sorunu çözecek yanıtı aşağıdaki gibi tüketin

response.getEntity().consumeContent();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.