Autore Topic: Problema velocità POST HttpUrlConnection  (Letto 3061 volte)

Offline mserioli

  • Nuovo arrivato
  • *
  • Post: 4
  • Respect: 0
    • Mostra profilo
Problema velocità POST HttpUrlConnection
« il: 25 Febbraio 2014, 18:19:28 CET »
0
Buongiorno a tutti.

Premetto che ho già postato questa domanda su stackoverflow http://stackoverflow.com/questions/21932685/android-httpurlconnection-post-multipart ma purtroppo non ho ancora ricevuto risposta.. ed il tepo stringe :)

Sto sviluppando una app per android e sto provando a fare il post di un file di test su server tomcat. Le prove sono state fatte su Android 4.1.2, ma ho verificato gli stessi problemi su 4.0.3.

Il problema è che l'upload del file richiede molto tempo.. circa 70 secondi per un file di 4MB (su rete locale.. con 3g richiede un po' più tempo).
Ho escluso fosse un problema del server. Con curl infatti copio lo stesso file in pochi secondi dal mio pc. Ho provato anche ad effettuare il post ad uno script php su apache, ma il problema è lo stesso..

Sto utilizzando la classe RestTemplate Spring Android RestClient 1.0.1.RELEASE e AndroidAnnotations (ma non credo quest'ultimo dettaglio sia importante). Data la versione di android ed il fatto che non effettuo forzature sul client http di default, spring android utilizza HttpUrlConnection per fare le varie richieste http.

Ho inoltre esteso la classe ClientHttpRequestFactory in modo da impostare alcuni parametri della connessione SSL ed ho esteso la classe ClientHttpRequestInterceptor per aggiungere gli header di autenticazione alla richiesta http.

Ho anche impostato sulla connessione il parametro setBufferRequestBody(false) per evitare eccezioni del tipo OutOfMemoryException sull'invio di file di grandi dimensioni, ma questo non influisce sui tempi di trasferimento del file.

Ecco la classe che estende ClientHttpRequestFactory:

Codice (Java): [Seleziona]
public class MyClientHttpRequestFactory extends SimpleClientHttpRequestFactory{

    @Override
    protected void prepareConnection(HttpURLConnection connection,  String httpMethod) throws IOException {
        super.prepareConnection(connection, httpMethod);
        connection.setConnectTimeout(240 * 1000);
        connection.setReadTimeout(240 * 1000);


        if ("post".equals(httpMethod.toLowerCase())) {
            setBufferRequestBody(false);
        }else {
            setBufferRequestBody(true);
        }
    }


@Override
protected HttpURLConnection openConnection(URL url, Proxy proxy) throws IOException {
    final HttpURLConnection httpUrlConnection = super.openConnection(url, proxy);

    if (url.getProtocol().toLowerCase().equals("https")
        &&
        settings.selfSignedCert().get())
    {
        try {
            ((HttpsURLConnection)httpUrlConnection).setSSLSocketFactory(getSSLSocketFactory());
            ((HttpsURLConnection)httpUrlConnection).setHostnameVerifier(new NullHostnameVerifier());
        } catch (Exception e) {
            MyLog.e(LOG_TAG, "OpenConnection", e);
        }
    }

    return httpUrlConnection;
}

Ed ecco la classe che estende ClientHttpRequestInterceptor:

Codice (Java): [Seleziona]
public class MyClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        final HttpHeaders headers = request.getHeaders();

        headers.setAuthorization(new HttpBasicAuthentication( settings.username().get(), settings.password().get()));

        if (settings.enable_gzip().get()) {
            headers.setAcceptEncoding(ContentCodingType.GZIP);
        }

        return execution.execute(request, body);
    }
}

Ed infine ecco la mia chiamata Rest:
Codice: [Seleziona]
List<ClientHttpRequestInterceptor> interceptors = Arrays.asList((ClientHttpRequestInterceptor)myClientHttpRequestInterceptor);

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.setInterceptors(interceptors);

MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
parts.add("file", new FileSystemResource("/sdcard/test/4MB_file"));

HttpEntity<MultiValueMap> requestEntity = new HttpEntity<MultiValueMap>(parts);
restTemplate.exchange(myUrl, HttpMethod.POST, requestEntity, Integer.class).getBody();

}

Effettuando debug sulla libreria Spring Android queste sono le istruzioni secondo me più significative eseguite:

Codice (Java): [Seleziona]
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
    HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
    prepareConnection(connection, httpMethod.name());
    if (this.bufferRequestBody) {
        return new SimpleBufferingClientHttpRequest(connection);
    } else {
        return new SimpleStreamingClientHttpRequest(connection, this.chunkSize);
    }
}

Poiché
Codice (Java): [Seleziona]
this.bufferRequestBody è false, viene ritornato
Codice (Java): [Seleziona]
new SimpleStreamingClientHttpRequest(connection, this.chunkSize); (dove chunkSize = 0)

Codice (Java): [Seleziona]
SimpleStreamingClientHttpRequest(HttpURLConnection connection, int chunkSize) {
    this.connection = connection;
    this.chunkSize = chunkSize;

    // Bugs with reusing connections in Android versions older than Froyo (2.2)
    if (olderThanFroyo) {
        System.setProperty("http.keepAlive", "false");
    }
}

ed infine:

Codice (Java): [Seleziona]
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());

delegate.getHeaders().putAll(request.getHeaders());

if (body.length > 0) {
    FileCopyUtils.copy(body, delegate.getBody());
}
return delegate.execute();

Da qui in poi non vedo più cosa succede.. penso sia delegato il tutto al s.o.

Ho fatto un dump del traffico tcp generato dal tablet:

Codice: [Seleziona]
POST /urlWherePost HTTP/1.1
Content-Type: multipart/form-data;boundary=nKwsP85ZyyzSDuAqozCTuZOSxwF1jLAtd0FECUPF
Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxx=
Accept-Encoding: gzip
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.1.2; sdk Build/MASTER)
Host: 192.168.168.225:8080
Connection: Keep-Alive
Content-Length: 4096225

--nKwsP85ZyyzSDuAqozCTuZOSxwF1jLAtd0FECUPF
Content-Disposition: form-data; name="file"; filename="4MB_file"
Content-Type: application/octet-stream
Content-Length: 4096000

Ho provato anche a ricreare con curl una richiesta con header simili, per riprodurre il problema:

Codice: [Seleziona]
curl --verbose
    -H "Connection: Keep-Alive"
    -H "Content-Type: multipart/form-data"
    -H "Accept-Encoding: gzip"
    -H "Content-Disposition: form-data; name=\"file\"; filename=\"4MB_file\""
    -H "Content-Type: application/octet-stream"
    --user xxx:xxx
    -X POST
    --form file=@4MB_file
    [url]http://192.168.168.225:8080/urlWherePost

Ma non riesco a ricreare il problema.

I problemi sono quindi solo sul POST di file di grossi dimensioni. Il normale scambio di dati (json) / download di file funziona regolarmente.


Inoltre, guardando il tool DDMS nella tab Network Statistics ho notato che non supero picchi di 250kb in TX. Sembra che ci sia un collo di bottiglia.. ma non riesco a capire dove..

Qualcuno può aiutarmi.. dirmi dove e cosa guardare, darmi consigli, varie / eventuali?

Grazie mille e buona giornata a tutti
Marco

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:Problema velocità POST HttpUrlConnection
« Risposta #1 il: 25 Febbraio 2014, 18:36:10 CET »
0
Hai provato a creare un form PHP di upload e fare upload degli stessi 4MB da quello?
Ovviamente aprendo il form con un browser per android (stock, firefox, chrome o altri).

A occhio, se ti risulta throttled vuol dire che il collo di bottiglia sta nel tablet, altrimenti nella libreria o nel modo di usarla.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline mserioli

  • Nuovo arrivato
  • *
  • Post: 4
  • Respect: 0
    • Mostra profilo
Re:Problema velocità POST HttpUrlConnection
« Risposta #2 il: 25 Febbraio 2014, 18:41:18 CET »
0
Grazie per la risposta!!

Mi spiego meglio..

Ho creato una pagina php che intercetta la richiesta effettuata da tablet tramite HttpUrlConnection.. per verificare che non fosse un problema di tomcat.

Il post a questa pagina php da curl impiega pochi secondi, mentre da tablet circa 70 secondi.

Off-Topic:
se ti risulta throttled vuol dire che il collo di bottiglia sta nel tablet

Dove posso vedere il throttled? Scusa ma mi sfugge!

Grazie
« Ultima modifica: 25 Febbraio 2014, 19:47:33 CET da mserioli »

Offline bradipao

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 4043
  • keep it simple
  • Respect: +567
    • Github
    • Google+
    • bradipao
    • Mostra profilo
  • Dispositivo Android:
    Nexus 5
  • Play Store ID:
    Bradipao
  • Sistema operativo:
    W7
Re:Problema velocità POST HttpUrlConnection
« Risposta #3 il: 25 Febbraio 2014, 21:32:11 CET »
0
Premetto che non so se applicabile al tuo caso.

Intendevo dire: crea un form di upload nel server apache, in modo che esegua il POST di upload del file all'indirizzo voluto. Apri chrome sul tablet, apri l'url del form, seleziona un file ed esegui il POST di upload dal browser verso l'indirizzo volut. Lo scopo è vedere se un POST fatto dal browser verso il medesimo destinatario ha le stesse limitazioni (throttling) della tua implementazione.
NON rispondo a domande nei messaggi privati
Bradipao @ Play Store

Offline mserioli

  • Nuovo arrivato
  • *
  • Post: 4
  • Respect: 0
    • Mostra profilo
Re:Problema velocità POST HttpUrlConnection
« Risposta #4 il: 26 Febbraio 2014, 09:04:45 CET »
0
Grazie della risposta.

Da browser il post è rapido.. ma non capisco cosa possa esserci di sbagliato nell'utilizzo della libreria.. mah.