使用长连接能够减少建立销毁连接的消耗,三次握手、四次挥手对性能影响是很大的。一般 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;
关于长连接,有两个参数需要注意:
- keepalive_timeout:连接不活跃多长时间后断开,如果服务端(nginx)设置的时间比客户端短的话,可能会导致客户端请求异常。前提是 HTTPClient 都 http.connection.stalecheck 参数置为 false,也就是不在请求前检查连接的有效性。
- keepalive_requests:用于设置一个keep-alive连接上可以服务的请求的最大数量。当最大请求数量达到时,连接被关闭。nginx 和 Tomcat 上默认都是100,对于 QPS 较高的应用来说 100 有些太小了。
以上两个参数在 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 异常,需要对其进行特殊处理,进行适当的重试。
资料