我们正在项目中使用内部HttpServer
类来通过HTTP在客户端和服务器之间交换数据。当我们切换到Java 7时,我们意识到结果交付的延迟。我们可以将问题减少到以下示例:
类EchoServer
创建上下文/echo
,它只返回每个请求的当前日期和请求URI。然后,客户端在循环中调用此服务。
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();
}
}
}
以下客户端使用类URL
在无限循环中调用服务,并打印返回流中的第一个字符(将是<
符号)。此外,客户端打印当前时间。
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());
}
}
}
如果在Java6上执行此代码,一切正常,大约打印结果。每5毫秒。
% 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
如果代码在Java7上执行,则每个请求使用大约1000毫秒
% 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
似乎在某处隐藏了1000毫秒的超时。如果在InputStreamReader
上而不是BufferedReader
上读取该字符,则会发生相同的延迟。
如果直接从输入流中读取一个字节,则无法看到延迟。另一方面,如果EchoClient
程序是
针对servlet运行,然后一切正常,与是否使用BufferedReader
或InputStreamReader
无关。
似乎,类InputStreamReader
期待来自服务器的东西,Java 7实现不再提供
的HttpServer。你知道这里到底发生了什么,以及如何解决这个问题?解决方法?或者这是一个错误吗?
谢谢!
我在客户端代码中添加了更多时间:
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());
}
}
具有以下结果:
% 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
openStream
的第一次调用需要一些时间(70ms),但openStream
的所有进一步调用需要更长的时间(约996ms)。
答案 0 :(得分:1)
刚刚向Oracle提交了一份错误报告。对于两个Java版本(SE 6或7),我的延迟时间为38毫秒。
/**
* @test
* @bug
* @summary pipelining delay on Ubuntu 12.04.01 LTS / amd64
*/
import com.sun.net.httpserver.*;
import java.util.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
public class Bug {
static int iterations = 20;
static long requiredMinimumDelay = 10L;
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetSocketAddress addr = new InetSocketAddress (0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
long minDelay = requiredMinimumDelay * 1000L;
try {
for(int i = 0; i < iterations; i++) {
URL url = new URL ("http://localhost:"+server.getAddress().getPort()+"/test/foo.html");
HttpURLConnection urlc = (HttpURLConnection)url.openConnection ();
InputStream is = urlc.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String res = br.readLine();
br.close();
// skip first few
if(i < iterations/2) {
continue;
}
long delay = System.currentTimeMillis() - Long.parseLong(res);
System.out.println("delay: "+delay+" ms");
if(delay < minDelay) {
minDelay = delay;
}
}
} catch (Exception ex) {}
server.stop(2);
executor.shutdown();
if(minDelay > requiredMinimumDelay) {
throw new Exception("minimum delay too large: "+minDelay);
}
}
static class Handler implements HttpHandler {
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read () != -1) ;
is.close();
String response = Long.toString(System.currentTimeMillis())+"\n";
t.sendResponseHeaders (200, response.length());
OutputStream os = t.getResponseBody();
os.write (response.getBytes());
t.close();
}
}
}
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8009548
更新:事实证明,甲骨文把它归类为“两个不同的错误”,一个用于38毫秒(他们用的是什么?)和一个用于1000毫秒的一个,他们在这里解决了:
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8014254
因此1000ms版本有望在基于后端链接的版本“8b91”和“7u85”中修复。
答案 1 :(得分:1)
我遇到了同样的问题,但user1050755的评论指出了bug filled并且它有一个解决方案:
...当服务器使用线程池时,这不是问题,但对于单线程服务器,此超时会导致瓶颈。
所以,制作一个多线程服务器:
final Executor multi = Executors.newFixedThreadPool(10);
final HttpServer server = HttpServer.create(new InetSocketAddress(s_HTTP_PORT), 5);
//... do your REST bindings here
server.setExecutor(multi);
server.start();
对我来说就像一个魅力。
PS。像&#34; com.sun.net.httpserver这样的评论非常糟糕&#34;不提供任何帮助 - 它与#34相同;而是使用Apache而不是#34;
答案 2 :(得分:0)
您似乎没有关闭url.openStream()
返回的BufferedReader或InputStream。不关闭流可能会导致在后续迭代中重用连接的问题(并且通常是错误的行为)。
通过明确调用rd.close()
和stream.close()
,您有不同的结果吗?
答案 3 :(得分:0)
解决方法(最初来自user1050755)在sendResponseHeaders()方法之前添加了这个:
httpExchange.getResponseHeaders().add("Connection", "close");
基本上禁用&#34;保持活力&#34;功能,但至少对我来说,每个请求从1000毫秒到50毫秒,因为我没有选择轻松升级我的JRE。虽然它确实失去了#34;保持活力&#34;功能FWIW。