我有以下示例代码,它启动内置 Java http 服务器,使用内置 Java http 客户端向其发出请求,然后尝试停止服务器。
问题是:
stop(delay)
方法会精确阻塞delay
秒,无论延迟如何。
我希望此方法能够处理所有传入请求并关闭服务器。因此,在这种情况下,我希望该方法立即关闭,因为没有传入请求。
我可以使用
stop(0)
,但是它对于生产服务器来说似乎并不理想,因为它不会给当前请求完成的机会。
package test;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.LocalDateTime;
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
var httpServer = HttpServer.create();
httpServer.createContext("/", exchange -> {
exchange.sendResponseHeaders(200, -1);
exchange.close();
});
httpServer.bind(new InetSocketAddress("127.0.0.1", 0), 0);
httpServer.start();
var httpClient = HttpClient.newHttpClient();
var host = httpServer.getAddress().getAddress().getHostAddress();
var port = httpServer.getAddress().getPort();
var httpRequest = HttpRequest.newBuilder()
.uri(URI.create("http://" + host + ":" + port + "/"))
.GET()
.build();
var httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofByteArray());
System.out.println(httpResponse.statusCode());
System.out.println(LocalDateTime.now() + " Stopping server");
httpServer.stop(10);
System.out.println(LocalDateTime.now() + " Server stopped");
}
}
200
2023-07-29T18:37:25.492361 Stopping server
2023-07-29T18:37:35.668797 Server stopped
这是停止方法的 javadoc:
/**
* Stops this server by closing the listening socket and disallowing
* any new exchanges from being processed. The method will then block
* until all current exchange handlers have completed or else when
* approximately <i>delay</i> seconds have elapsed (whichever happens
* sooner). Then, all open TCP connections are closed, the background
* thread created by {@link #start()} exits, and the method returns.
* Once stopped, a {@code HttpServer} cannot be re-used.
*
* @param delay the maximum time in seconds to wait until exchanges have finished
* @throws IllegalArgumentException if delay is less than zero
*/
据我了解,如果当前没有请求执行,则
stop(10)
方法不应该等待 10 秒。此超时是为了在关闭套接字之前为当前正在执行的请求提供完成时间。
stop
的 Javadoc 说:
该方法将阻塞,直到所有当前交换处理程序完成,或者大约延迟秒数过去后(以较早发生者为准)。
这对我来说看起来像是一个错误,正如
ServerImpl.stop()
所做的那样:
terminating = true;
try { schan.close(); } catch (IOException e) {}
selector.wakeup();
long latest = System.currentTimeMillis() + delay * 1000;
while (System.currentTimeMillis() < latest) {
delay();
if (finished) {
break;
}
}
所以只有当
finished
是true
时才会提前返回。
finished
字段只能在ServerImpl.Dispatcher.handleEvent
中设置为true:
if (r instanceof WriteFinishedEvent) {
logger.log(Level.TRACE, "Write Finished");
int exchanges = endExchange();
if (terminating && exchanges == 0) {
finished = true;
}
但是
terminating
将为 false,除非在调用 WriteFinishedEvent
之后发生 stop
。
我们可以稍微更改程序以获得预期的行为,方法是确保请求在调用
stop()
之前发出,但在调用 stop()
之后完成:
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
var httpServer = HttpServer.create();
httpServer.createContext("/", exchange -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
exchange.sendResponseHeaders(200, -1);
System.out.println("Did request");
exchange.close();
});
httpServer.bind(new InetSocketAddress("127.0.0.1", 0), 0);
httpServer.start();
Executors.newSingleThreadExecutor().submit(() -> {
var httpClient = HttpClient.newHttpClient();
var host = httpServer.getAddress().getAddress().getHostAddress();
var port = httpServer.getAddress().getPort();
var httpRequest = HttpRequest.newBuilder()
.uri(URI.create("http://" + host + ":" + port + "/"))
.GET()
.build();
HttpResponse<byte[]> httpResponse = null;
try {
httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofByteArray());
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(httpResponse.statusCode());
});
Thread.sleep(500);
System.out.println(LocalDateTime.now() + " Stopping server");
httpServer.stop(10);
System.out.println(LocalDateTime.now() + " Server stopped");
}
}