关闭 Apache httpcomponents 阻塞 HTTP 服务器的正确方法
Proper way to shutdown Apache httpcomponents blocking HTTP server
我有 Apache HttpComponents 4.4.1 同步服务器 的应用程序,它运行多个 HTTP "services",对于它们中的每一个,我在不同的端口上启动 HTTP 服务器。当用户决定停止服务时,我使用以下代码关闭此端口上的 HTTP 服务器 运行:
org.apache.http.impl.bootstrap.HttpServer server;
....
public void stopServer(){
server.shutdown(42, TimeUnit.MICROSECONDS);
}
我在 Linux 上遇到以下问题:如果打开了 Keep-alive 连接(没有请求处理),这些套接字不会关闭。仅关闭 ServerSocket:
netstat -aon | grep 58276
TCP 127.0.0.1:50658 127.0.0.1:58276 ESTABLISHED 18012
TCP 127.0.0.1:58276 127.0.0.1:50658 ESTABLISHED 18012
试图在同一端口上再次启动 HTTP 服务器时抛出 BindingException:
Caused by: java.net.BindException: Address already in use
at java.net.PlainSocketImpl.socketBind(Native Method)
at java.net.PlainSocketImpl.socketBind(PlainSocketImpl.java:521)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:414)
at java.net.ServerSocket.bind(ServerSocket.java:326)
at java.net.ServerSocket.<init>(ServerSocket.java:192)
at javax.net.DefaultServerSocketFactory.createServerSocket(ServerSocketFactory.java:170)
at org.apache.http.impl.bootstrap.HttpServer.start(HttpServer.java:116)
在 Windows 上,行为相同,但是没有 BindingException,服务器开始处理请求时没有任何问题。
你的问题是连接没有关闭。 HTTP 规范没有指定持久连接应该保持多长时间。
Some HTTP servers use a non-standard Keep-Alive
header to
communicate to the client the period of time in seconds they intend to
keep the connection alive on the server side. HttpClient
makes use
of this information if available. If the Keep-Alive
header is not
present in the response, HttpClient
assumes the connection can be
kept alive indefinitely.
因此他们建议创建您的 KeepAliveStrategy
以便在特定时间后终止这些连接:
ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
}
}
}
HttpHost target = (HttpHost) context.getAttribute(
HttpClientContext.HTTP_TARGET_HOST);
if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
// Keep alive for 5 seconds only
return 5 * 1000;
} else {
// otherwise keep alive for 30 seconds
return 30 * 1000;
}
}
};
CloseableHttpClient client = HttpClients.custom()
.setKeepAliveStrategy(myStrategy)
.build();
原来是一个错误:https://issues.apache.org/jira/browse/HTTPCORE-420
oleg 已经提出了一个修复方案。正式发布后我会更新这个
我有 Apache HttpComponents 4.4.1 同步服务器 的应用程序,它运行多个 HTTP "services",对于它们中的每一个,我在不同的端口上启动 HTTP 服务器。当用户决定停止服务时,我使用以下代码关闭此端口上的 HTTP 服务器 运行:
org.apache.http.impl.bootstrap.HttpServer server;
....
public void stopServer(){
server.shutdown(42, TimeUnit.MICROSECONDS);
}
我在 Linux 上遇到以下问题:如果打开了 Keep-alive 连接(没有请求处理),这些套接字不会关闭。仅关闭 ServerSocket:
netstat -aon | grep 58276
TCP 127.0.0.1:50658 127.0.0.1:58276 ESTABLISHED 18012
TCP 127.0.0.1:58276 127.0.0.1:50658 ESTABLISHED 18012
试图在同一端口上再次启动 HTTP 服务器时抛出 BindingException:
Caused by: java.net.BindException: Address already in use
at java.net.PlainSocketImpl.socketBind(Native Method)
at java.net.PlainSocketImpl.socketBind(PlainSocketImpl.java:521)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:414)
at java.net.ServerSocket.bind(ServerSocket.java:326)
at java.net.ServerSocket.<init>(ServerSocket.java:192)
at javax.net.DefaultServerSocketFactory.createServerSocket(ServerSocketFactory.java:170)
at org.apache.http.impl.bootstrap.HttpServer.start(HttpServer.java:116)
在 Windows 上,行为相同,但是没有 BindingException,服务器开始处理请求时没有任何问题。
你的问题是连接没有关闭。 HTTP 规范没有指定持久连接应该保持多长时间。
Some HTTP servers use a non-standard
Keep-Alive
header to communicate to the client the period of time in seconds they intend to keep the connection alive on the server side.HttpClient
makes use of this information if available. If theKeep-Alive
header is not present in the response,HttpClient
assumes the connection can be kept alive indefinitely.
因此他们建议创建您的 KeepAliveStrategy
以便在特定时间后终止这些连接:
ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
}
}
}
HttpHost target = (HttpHost) context.getAttribute(
HttpClientContext.HTTP_TARGET_HOST);
if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
// Keep alive for 5 seconds only
return 5 * 1000;
} else {
// otherwise keep alive for 30 seconds
return 30 * 1000;
}
}
};
CloseableHttpClient client = HttpClients.custom()
.setKeepAliveStrategy(myStrategy)
.build();
原来是一个错误:https://issues.apache.org/jira/browse/HTTPCORE-420 oleg 已经提出了一个修复方案。正式发布后我会更新这个