使用spring 3.2.0.RELEASE resttemplate和httpcomponents 4.2.3进行rest调用。内存占用量一直稳定增长,直到达到最大值。

以下是配置:

<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>


HttpClient:

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());
    }
}
}


TautologicX509TrustManager:

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对象?

最佳答案

回复较晚,但此答案供该问题的将来访问者使用。之所以会在堆中看到大量SSLSocketImpl并随后被垃圾回收的原因,是因为SSLSessionContext支持缓存SSL连接,可以在不同的TCP连接之间重用SSL连接,以减少握手后协商临时加密密钥时的握手开销。实际的TCP连接已建立。

默认情况下,SSLSessionContext会话数不受限制,默认时间段为24小时,这意味着您可能会在高流量下最终使用大量堆内存。一种方法是设置适合您的应用程序需求的缓存大小。这是一个例子:

sslContext.getServerSessionContext().setSessionCacheSize(1000);

10-05 21:35