关闭 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 规范没有指定持久连接应该保持多长时间。

来自documentation

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 已经提出了一个修复方案。正式发布后我会更新这个