Stiamo utilizzando la classe interna HttpServer
in un progetto per lo scambio di dati tra un client e un server su HTTP. Quando siamo passati a Java 7, abbiamo realizzato un ritardo nella consegna dei risultati. Potremmo ridurre il problema al seguente esempio:Ritardo 1s in HttpServer da Java 7
Classe EchoServer
crea il contesto /echo
che restituisce semplicemente la data corrente e l'URI della richiesta ad ogni richiesta. Questo servizio viene quindi richiamato da un client in un ciclo.
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Date;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class EchoServer {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(80), 0);
server.createContext("/echo", new EchoHandler());
server.start();
}
static class EchoHandler implements HttpHandler {
public void handle(HttpExchange httpExchange) throws IOException {
httpExchange.getResponseHeaders().add("Content-type", "text/html");
String response = "<b>" + new Date() + "</b> for " + httpExchange.getRequestURI();
httpExchange.sendResponseHeaders(200, response.length());
OutputStream os = httpExchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}
Il seguente client invoca il servizio in un ciclo infinito con classe URL
e stampa il primo carattere del flusso restituito (che sarà il segno <
). Inoltre, il cliente stampa l'ora corrente.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
public class EchoClient {
public static void main(String[] args) throws Exception{
while(true) {
URL url = new URL("http://localhost:80/echo");
BufferedReader rd = new BufferedReader(new InputStreamReader(url.openStream()));
int res = rd.read();
System.out.println((char)res);
System.out.println(System.currentTimeMillis());
}
}
}
Se questo codice viene eseguito su Java6, tutto funziona correttamente e un risultato viene stampato ca. ogni 5 ms.
% java -version
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02, mixed mode)
% java EchoClient
<
1362515635677
<
1362515635682
<
1362515635687
<
1362515635691
Se il codice viene eseguito su Java7, ogni richiesta utilizza 1000 ms circa.
% java -version
java version "1.7.0_17"
Java(TM) SE Runtime Environment (build 1.7.0_17-b02)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)
% java EchoClient
<
1362517297845
<
1362517298844
<
1362517299845
<
1362517300845
Sembra che un timeout di 1000 ms sia nascosto da qualche parte. Se il carattere viene letto su InputStreamReader
anziché su BufferedReader
, si verifica lo stesso ritardo. Se un byte viene letto direttamente dal flusso di input, non è possibile visualizzare alcun ritardo. D'altra parte, se il programma EchoClient
è eseguito su un servlet, allora tutto funziona correttamente, indipendentemente dal fatto che sia utilizzato lo BufferedReader
o lo InputStreamReader
.
Sembra che la classe InputStreamReader
si aspetti qualcosa dal server che non è più fornito dall'implementazione di Java 7 di HttpServer. Hai un'idea di cosa succede esattamente qui e di come questo problema possa essere risolto? Una soluzione? O questo è un bug?
Grazie!
ho aggiunto ulteriori tempi per il codice del client:
public static void main(String[] args) throws Exception{
while(true) {
System.out.println("0: "+System.currentTimeMillis());
URL url = new URL("http://localhost:80/echo");
System.out.println("1: "+System.currentTimeMillis());
InputStream in = url.openStream();
System.out.println("2: "+System.currentTimeMillis());
InputStreamReader isr = new InputStreamReader(in);
System.out.println("3: "+System.currentTimeMillis());
char res = (char)isr.read(); // character read is `<`
System.out.println(res + ": "+System.currentTimeMillis());
}
}
con il seguente risultato:
% java EchoClient
0: 1362532555535
1: 1362532555537
2: 1362532555608
3: 1362532555609
<: 1362532555611
0: 1362532555612
1: 1362532555613
2: 1362532556608
3: 1362532556609
<: 1362532556610
0: 1362532556611
1: 1362532556612
2: 1362532557609
3: 1362532557610
<: 1362532557611
0: 1362532557612
1: 1362532557613
la prima chiamata del openStream
richiede un certo tempo (70ms), ma tutti ulteriori invocazioni di openStream
richiedono molto più tempo (996ms circa).
In realtà, com.sun.net.httpserver non fa parte dell'API Java, e Penso che non dovrebbe essere usato in progetti mission-critical. Puoi provare alternative come NanoHTTPD: http://elonen.iki.fi/code/nanohttpd/ – gd1
Questo è molto interessante ma difficile da credere. Puoi inserire più debug di timestamp per trovare esattamente quale parte di codice impiega 1 sec? – irreputable
è la chiamata a 'url.openStream()' che richiede 1000ms, ma solo alla sua seconda (e successiva) chiamata nel ciclo, e solo se successivamente viene generato un 'InputStreamReader' e viene usato per leggere un byte. Pertanto, 'BufferedReader' sembra innocente. – Dominik