最近项目需要实现HTTP的长连接,由于项目的web服务是由tomcat搭建的,所以在server端侧实现十分简单,在server.xml文件中追加一下配置即可:
<Connector port="443"
...略...
keepAliveTimeout="300000"
maxKeepAliveRequests="100"
...略.../>
keepAliveTimeout:服务端对长连接的保持时间
maxKeepAliveRequests:每个长连接能发送请求的最大数,超过就需要重新建立连接。
而我们作为客户端发起请求的时候就不是特别顺利,因为一开始客户端是用HttpsURLConnection类实现的,网上查了一番资料,没有发现实现http长连接的方法。于是改为使用httpClient,apache的一个开源项目,使用中发现这货功能十分强大。
以下是客户端的实现(例):
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.http.HttpRequest;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.pool.PoolStats;
public class ConnectionManager {
// get connect from connection pool time out
private static int connectionRequestTimeout = 10000;
// connect time out
private static int connectionTimeout = 10000;
// read time out
private static int socketTimeout = 10000;
private static CloseableHttpClient httpClient = null;
private static PoolingHttpClientConnectionManager connectionManager;
private static SSLConnectionSocketFactory ssf;
private static RequestConfig defaultRequestConfig;
private static X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
public static void init() {
enableSSL(false);
defaultRequestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(connectionRequestTimeout)
.setConnectTimeout(connectionTimeout)
.setSocketTimeout(socketTimeout).build();
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", ssf).build();
connectionManager = new PoolingHttpClientConnectionManager(
socketFactoryRegistry);
// default : 每个路由基础上的连接不超过2个,总连接数不能超过20
// 将最大连接数 50
connectionManager.setMaxTotal(50);
// 每个路由基础的连接 25
connectionManager.setDefaultMaxPerRoute(25);
httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(defaultRequestConfig).build();
}
public static PoolingHttpClientConnectionManager getConnectionManager() {
return connectionManager;
}
private static void checkClient() {
if (httpClient == null) {
httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(defaultRequestConfig).build();
}
}
private static void enableSSL(boolean trustCerts) {
if (trustCerts) {
// do not need to Verify certificate
try {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{tm}, null);
ssf = new SSLConnectionSocketFactory(ctx,
NoopHostnameVerifier.INSTANCE);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
} else {
// need to Verify certificate
KeyStore ts = null;
TrustManagerFactory tmf = null;
try {
ts = KeyStore.getInstance(("JKS"));
ts.load(new FileInputStream(new File("truststore.jks")),
"aTte3yZ*8osb".toCharArray());
tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
ssf = new SSLConnectionSocketFactory(ctx,
NoopHostnameVerifier.INSTANCE);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
}
public static CloseableHttpResponse doHttpsGet(String url,
Map<String, String> headers) throws IOException {
checkClient();
HttpRequest httpGet = new HttpGet(url);
if (headers != null && !headers.isEmpty()) {
httpGet = setHeaders(headers, httpGet);
}
CloseableHttpResponse response = httpClient.execute((HttpGet) httpGet);
return response;
}
public static CloseableHttpResponse doHttpsPost(String url,
Map<String, String> headers, String data) throws IOException {
checkClient();
HttpRequest httpRequest = new HttpPost(url);
if (headers != null && !headers.isEmpty()) {
httpRequest = setHeaders(headers, httpRequest);
}
CloseableHttpResponse response = null;
try {
HttpPost httpPost = (HttpPost) httpRequest;
httpPost.setEntity(new StringEntity(data,
ContentType.create("application/json", "UTF-8")));
response = httpClient.execute(httpPost);
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
private static HttpRequest setHeaders(Map<String, String> headers,
HttpRequest request) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
request.addHeader((String) entry.getKey(),
(String) entry.getValue());
}
return request;
}
public static void getConnectioNum() {
PoolStats stats = connectionManager.getTotalStats();
System.out.println(new Date() + " - " + stats.toString());
}
}
// end