使用长连接能够减少建立销毁连接的消耗,三次握手、四次挥手对性能影响是很大的。一般 RPC 如 Dubbo 默认都是长连接的,HTTP 1.1 之上也可以支持长连接了,HTTP 2.0 也支持了单一长连接的多路复用。
一般 HTTP 服务前面都会挂 nginx 做负载均衡,那么长连接的设置也分为从客户端到 nginx、从 nginx 到服务端两部分。
如果使用 Java 的 apache HTTPClient 可以通过设置 ConnectionKeepAliveStrategy 和协议为 HTTP1.1 来使用长连接,对于 4.2.x 版本设置方法如下所示:
private static final ConnectionKeepAliveStrategy KEEP_ALIVE_STRATEGY = new ConnectionKeepAliveStrategy() { public long getKeepAliveDuration(HttpResponse response, HttpContext context) { 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 { // 如果 response header 里有 Keep-Alive:timeout 参数,则使用其值作为长连接超时时间 return Long.parseLong(value) * 1000; } catch (NumberFormatException ignore) { } } } return 30 * 1000; // 30s } }; HttpParams params = new BasicHttpParams(); // 指定使用 http1.1,1.1里默认使用长连接 HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); //创建连接池 PoolingClientConnectionManager cm = new PoolingClientConnectionManager(registry, 1, TimeUnit.MINUTES); cm.setDefaultMaxPerRoute(10); cm.setMaxTotal(200); httpClient = new DefaultHttpClient(cm, params); // 设置超时时间 httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 100); httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 100); httpClient.getParams().setParameter(ClientPNames.CONN_MANAGER_TIMEOUT, 100); httpClient.setKeepAliveStrategy(KEEP_ALIVE_STRATEGY);
在 NGINX 请求后端服务时启用长连接需要配置:
proxy_http_version 1.1; proxy_set_header Connection ""; keepalive_timeout 120s 120s; keepalive_requests 10000;
关于长连接,有两个参数需要注意:
以上两个参数在 nginx、服务端都有,当 QPS 比较高时就需要做适当调整以减少或避免连接都频繁断开、建立。如果服务端使用的 Tomcat 容器,可以在 server.xml 文件里修改配置。
在 Spring Boot 2.x 里修改长连接的配置方法如下所示:
@Configuration public class TomcatCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> { @Override public void customize(TomcatServletWebServerFactory factory) { factory.addConnectorCustomizers(connector -> { AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) connector.getProtocolHandler(); protocol.setMaxKeepAliveRequests(10000); protocol.setKeepAliveTimeout(60000); // 长连接超时时间,单位 ms }); } }
上面提到 HTTPClient 的 http.connection.stalecheck 参数用于设置是否在发送请求之前检查连接有效性,如果每次发请求都检查对性能影响也会较大,可以去掉该参数。但是去掉之后,在连接被 NGINX 关闭后依然发请求则会导致 NoHttpResponseException 异常,需要对其进行特殊处理,进行适当的重试。
资料