Używamy wewnętrznej klasy HttpServer
w projekcie do wymiany danych między klientem a serwerem za pośrednictwem protokołu HTTP. Po przejściu na platformę Java 7 uzyskaliśmy opóźnienie w dostarczaniu wyników. Możemy zmniejszyć problem do następującej próbki:1s Opóźnienie w HttpServer od Javy 7
Klasa EchoServer
tworzy kontekst /echo
, który po prostu zwraca bieżącą datę i identyfikator URI żądania po każdym żądaniu. Ta usługa jest następnie wywoływana przez klienta w pętli.
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();
}
}
}
Poniższy klient wywołuje usługę w nieskończonej pętli przy użyciu klasę URL
i drukuje pierwszy znak od zwracanej strumienia (który będzie znakiem <
). Ponadto klient drukuje aktualny czas.
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());
}
}
}
Jeśli ten kod zostanie wykonany na Javie6, wszystko działa poprawnie, a wynik zostanie wydrukowany w przybliżeniu. co 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
Jeśli kod jest wykonywany na Javie7, to każde żądanie używa około 1000ms.
% 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
Wygląda na to, że limit czasu wynoszący 1000ms jest gdzieś ukryty. Jeśli zamiast tego zostanie odczytany znak InputStreamReader
przez BufferedReader
, nastąpi to samo opóźnienie. Jeśli bajt jest odczytywany bezpośrednio ze strumienia wejściowego, nie widać opóźnienia. Z drugiej strony, jeśli program EchoClient
jest uruchamiany w stosunku do serwletu, wszystko działa poprawnie, niezależnie od tego, czy użyto BufferedReader
czy InputStreamReader
. Wydaje się, że klasa InputStreamReader
oczekuje czegoś od serwera, który nie jest już dostarczany przez implementację Java 7 z HttpServer. Czy masz pojęcie, co dokładnie się tutaj dzieje i jak można rozwiązać ten problem? Obejście? Czy to błąd?
Dzięki!
Dodałem kolejne czasy w kodzie klienckim:
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());
}
}
z następującym wynikiem:
% 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
Pierwsze wezwanie openStream
zajmuje trochę czasu (70ms), ale wszystkie dalsze wywołania o wartości openStream
trwają znacznie dłużej (około 996ms).
W rzeczywistości, com.sun.net.httpserver nie jest częścią Java API, a Myślę, że nie powinno się tego używać w projektach krytycznych dla misji. Możesz wypróbować alternatywy, takie jak NanoHTTPD: http://elonen.iki.fi/code/nanohttpd/ – gd1
Jest to bardzo interesujące, ale trudno w to uwierzyć. Czy możesz wstawić więcej debugowania sygnatury czasowej, aby znaleźć dokładnie, która część kodu zajmuje 1 sekundę? – irreputable
jest to wywołanie 'url.openStream()', które ma 1000ms, ale tylko w jego drugim (i późniejszym) wywołaniu w pętli, i tylko wtedy, gdy później 'InputStreamReader' jest generowany i używany do odczytu bajta. Tak więc 'BufferedReader' wydaje się być niewinny. – Dominik