• SpingBoot —— RestTemplate的配置


    背景:最近工作上搭建了一个中间系统,采用了RestTemplate框架调用第三系统restful接口,调用方采用轮询的方式调用本系统的相关接口,期间多次出现堆内存溢出,系统假死,通用java自带的java内存分析工具分析系统生成的dump文件发现有一对象一直没被回收,占用98%堆内存,使用MAT分析该对象所在的线程,发现是使用resttemplate的相关线程,及相关的线程产生的堆对象并没有随resttemplate的close后被清除掉,网上搜索相关资料显示,传统HttpClient 在高请求量和高并发情况,很容易出现堆内存溢出的情况,而使用Apache中的HttpClient的实例CloseableHttpClient很少发生该现象,而RestTemplate默认使用的是org.springframework.http.client.ClientHttpRequest,需在配置文件进行相关的替换;

    本文主要记录springboot中配置RestTemplate。

     1、添加依赖:

    <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.3</version>
    </dependency>

    2、配置CloseableHttpClient向相关属性,并设置定时清理连接

    Package com.**.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.HeaderElement;
    import org.apache.http.HeaderElementIterator;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.config.Registry;
    import org.apache.http.config.RegistryBuilder;
    import org.apache.http.conn.connectionKeepAliveStrategy;
    import org.apache.http.conn.socket.ConnectionSocketFactory;
    import org.apache.http.conn.socket.PlainConnectionSocketFactory;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.comn.ssl.TrustSelfsignedStrategy;
    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.Message.BasicHeaderELementIterator;
    import org.apache.http.protocol.HTTP;
    import org.apache.http.protocol.HttpContext;
    import org.apache.http.ssl.SSLContextBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.Scheduled;
    import java.security.KeyManagementException;
    import java.security.KeyStoreException;
    import java.security.NoSuchAlgorithmException;
    import java.util.concurrent.TimeUnit;
    
    /* *
    * Supports both HTTP and HTTPS * Uses a connect ion pool to re-use connect ions and save overhead of creat ing connect ions. * Has a custom connection keep-al ive strategy (to apply a default keep-alive if one isn't specified) * starts an idle connection monitor to cont inuously clean up stale connections.
    */ @Slf4j @Configuration public class HttpClientConfig { //Determines the timeout in milliseconds until a connection is established. private static final int CONNECT_TIMEOUT = 30000; //The timeout when requesting a connection from the connection manager. private static final int REQUEST_ TIMEOUT = 30000; //The timeout for waiting for data private static final int SOCKET_ TIMEOUT = 60000; private static final int MAX_ TOTAL CONNECTIONS = 50; private static final int DEFAULT KEEP ALIVE_ TIME_ MIlLIS = 20 * 1000; private static final int CLOSE_ IDLE_ CONNECTION WAIT_ TIME_ SECS = 30; @Bean public PoolingHttpClientConnectionManager poolingConnectionManager() { SSLContextBuilder builder = new SSLContextBuilder (); try { builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); }catch (NoSuchAlgorithmException | KeyStoreException e) { log.error ("Pooling Connection Manager Initialisation failure because of"+ e.getMessage(), e); } SSLConnectionSocketFactory sslsf = null; try{ sslsf = new SSLConnectionSocketFactory (builder.build()); } catch (KeyManagementException | NoSuchAlgorithmException e) { log.error("Pooling Connection Manager Initialisation failure because of" + e.GetMessage(), e); } Registry <ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder .create ().register ("https", sslsf) .register (id: "http", new PlainConnectionSocketFactory ()) .build (); PoolingHttpclientConnectionManager poolingConnectionManager = new PoolingHttpclientConnectionManager (socketFactoryRegistry); poolingConnectionManager.setMaxTotal(MAX_ TOTAL CONNECTIONS); return poolingConnectionManager; } @Bean public ConnectionKeepAliveStrategy connectionKeepAliveStrategy () { return new ConnectionKeepAliveStrategy (){ @override 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")) { return Long.parseLong(value) * 1000; } } return DEFAULT_ KEEP_ALIVE_TIME_MILlIS; } }; } @Bean public CloseableHttpClient httpClient () { RequestConfig requestConfig = RequestConfig.custom () .setConnectionRequestTimeout(REQUEST_TIMEOUT) .setConnectTimeout (CONNECT_TIMEOUT) .setSocketTimeout (SOCKET_TIMEOUT).build(); return Httpclients.custom () .setDefaultRequestConfig(requestConfig) .setConnectionManager (poolingConnectionManager ()) .setKeepAliveStrategy (connectionKeepAliveStrategy ()) .build (); } @Bean public Runnable idleConnectionMonitor (final PoolingHttpClientConnectionManager connectionManager){ return new Runnable() { @override @Scheduled(fixedDelay = 10000) public void run() { try { if (connectionManager != null) { log.trace("run IdleConnectionMonitor - Closing expired and idle connections... "); connectionManager.closeExpiredConnections(); connectionManager.closeIdleConnections(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS); } else log.info("run IdleConnectionMonitor - Http Client Connection manager is not initialised"); } catch (Exception e) { log.error("run IdleConnectionMonitor - Exception occurred.msg={}. ", e.getMessage()); } } }; } }

    3、替换RestTemplate中的默认httpClient

    Package com.**.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.client.Httpcomponentsclienthttprequestfactory;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.StringHttpMessageConverter;
    import org.springframework.scheduling.TaskScheduler;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
    import org.springframework.web.client.RestTemplate;
    import java.nio.charset.Charset;
    import java.util.Iterator;
    import java.util.List;
    
    
    @Slf4j
    @Configuration
    public class RestTemplateConfig {
    
        @Autowired
        CloseableHttpClient httpClient;
    
        @Bean
        public RestTemplate restTemplate () {
    
            RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
    
            /**
             * StringHttpMessogeConverter 默认使用 IS0-8859-编码,此处修改为 UTF-8
             */
            List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
            Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
            while (iterator.hasNext()) {
                HttpMessageConverter<?> converter = iterator.next();
                if (converter instanceof StringHttpMessageConverter) {
                    ((StringHttpMessageConverter) converter).setDefaultCharset(Charset.forName("UTF-8"));
                }
            }
            return restTemplate;
        }
    
        @Bean
        public HttpComponentsClientHttpRequestFactory clientHttpRequestfactory () {
            HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =  new HttpComponentsClientHttpRequestfactory();
            clientJttpRequestFactory.setHttpClient(httpClient);
            return clientHttpRequestFactory;
        }
    
        @Bean
        public TaskScheduler taskScheduler() {
            ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
            scheduler.setThreadNamePrefix("poolScheduler");
            scheduler.setPoolSize (50);
            return scheduler;
        }
    }

    4、在启动类中注入RestTemplate类:

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder){
           return builder.build();
    }

     至此,RestTemplate就可以正常使用了。

    参考资料:https://www.cnblogs.com/jimw/p/9037542.html

  • 相关阅读:
    【作业4】测试作业-兴趣问题清单
    【读后感3】高效程序员的45个习惯
    【作业3】关于C语言的问卷调查
    【作业2】价值观作业
    Spring的零配置
    Spring容器中bean的作用域
    Spring注入方式
    Spring整合Struts2
    my first go
    Struts2对ajax的支持
  • 原文地址:https://www.cnblogs.com/gdwkong/p/9538594.html
Copyright © 2020-2023  润新知