背景:最近工作上搭建了一个中间系统,采用了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@configurationpublic 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、替换testtemplate中的默认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@configurationpublic 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() { threadpoooltaskscheduler scheduler = new threadpoooltaskscheduler(); scheduler.setthreadnameprefix("poolscheduler"); scheduler.setpoolsize (50); return scheduler; }}
4、在启动类中注入resttemplate类:
@beanpublic resttemplate resttemplate(resttemplatebuilder builder){ return builder.build();}
至此,resttemplate就可以正常使用了。
以上就是springboot中配置resttemplate的方法的详细内容。
