使用 spring 3.2.0.RELEASE resttemplate 和 httpcomponents 4.2.3 进行休息调用。内存占用量稳步增加,直到达到最大值。
以下是配置:
<bean id="myRestTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<constructor-arg index="0">
<bean factory-bean="httpClient" factory-method="get"/>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
<bean id="httpClient" class="com.mycompany.myproject.common.rest.HttpClient">
<constructor-arg index="0" ref="myKeyserverCA" ></constructor-arg>
<constructor-arg index="1" value="${com.mycompany.myproject.security.client.keyPassword}" ></constructor-arg>
<constructor-arg index="2" value="${default.max.total.connections}" ></constructor-arg>
<constructor-arg index="3" value="${default.max.host.connections}" ></constructor-arg>
</bean>
<bean id="myKeyserverCA"
class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:${com.mycompany.myproject.security.client.keyStore}" />
<property name="password" value="${com.mycompany.myproject.security.client.keyStorePass}" />
</bean>
客户端:
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.CoreConnectionPNames;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import java.security.KeyStore;
public class HttpClient {
private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (60 * 1000);
private KeyStore keystore;
private String password;
private int MAX_TOTAL_CONNECTION;
private int MAX_PER_ROUTE;
public HttpClient(KeyStore keyStore, String keyPassword, int MAX_TOTAL_CONNECTION, int MAX_PER_ROUTE) {
this.keystore = keyStore;
this.password = keyPassword;
this.MAX_TOTAL_CONNECTION = MAX_TOTAL_CONNECTION;
this.MAX_PER_ROUTE = MAX_PER_ROUTE;
}
public org.apache.http.client.HttpClient get() {
PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(getSchemeRegistry(this.keystore, this.password));
connectionManager.setMaxTotal(MAX_TOTAL_CONNECTION);
connectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
connectionManager.closeExpiredConnections();
org.apache.http.client.HttpClient httpClient = new DefaultHttpClient(connectionManager);
httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, DEFAULT_READ_TIMEOUT_MILLISECONDS);
return httpClient;
}
private static SchemeRegistry getSchemeRegistry(KeyStore keyStore, String keyPassword) {
SchemeRegistry registry = new SchemeRegistry();
try{
TrustManager[] trustManagerArray = { new TautologicalX509TrustManager() };
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyPassword.toCharArray());
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(kmf.getKeyManagers(), trustManagerArray, null);
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(sslc, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
registry.register(new Scheme("https", 443, sslSocketFactory));
return registry;
}catch(Exception e){
throw new RuntimeException(e.getMessage());
}
}
}
TautologicalX509TrustManager:
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
public class TautologicalX509TrustManager implements X509TrustManager {
private static final X509Certificate[] EMPTY_CERTIFICATES = new X509Certificate [0];
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return EMPTY_CERTIFICATES;
}
}
在这个组件上运行负载测试后,我们看到很多 SSLSocketImpl 对象和 byte[]。对 SSLSocketImpl 的传入引用来自 Finalizer 对象。
当负载测试停止后我们在机器上执行 netstat 时,我们看不到任何与底层服务的开放 tcp 连接。然而,在负载测试期间,TIME_WAIT 状态的连接很多,ESTABLISHED 状态的连接很少,但在测试停止后它们都关闭了。
我们是否缺少任何关闭套接字的 API 调用?为什么我们的堆中有这么多 SSLSocketImpl 对象?