开发过程中发现,使用restTemplate请求接口,在部分压测情况下,会报Caused by: java.io.IOException: Broken pipe
1.错误原因:
服务端向前端socket连接管道写返回数据时 链接(pipe)却断开了
从应用角度分析,这是因为客户端等待返回超时了,主动断开了与服务端链接
连接数设置太小,并发量增加后,造成大量请求排队等待
内存是否足够多支持对应的并发量
网络延迟,是否有丢包
错误代码:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory requestFactory){
return new RestTemplate(requestFactory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(10000);
factory.setConnectTimeout(10000);
return factory;
}
}
使用:
@Autowired private RestTemplate restTemplate; HttpEntity entity = new HttpEntity<>(headers); ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
2.重新认识RestTemplate
2.1RestTemplate是Spring提供的用于访问Rest服务的客户端
2.2底层通过使用java.net包下的实现创建HTTP 请求
2.3 通过使用ClientHttpRequestFactory指定不同的HTTP请求方式,主要提供了两种实现方式
2.3.1 SimpleClientHttpRequestFactory(默认)
底层使用J2SE提供的方式,既java.net包提供的方式,创建底层的Http请求连接
主要createRequest 方法( 断点调试),每次都会创建一个新的连接,每次都创建连接会造成极大的资源浪费,而且若连接不能及时释放,会因为无法建立新的连接导致后面的请求阻塞
2.3.2HttpComponentsClientHttpRequestFactory
底层使用HttpClient访问远程的Http服务
3.解决方式
1.客户端每次请求都要和服务端建立新的连接,即三次握手将会非常耗时,通过http连接池可以减少连接建立与释放的时间,提升http请求的性能
2.Spring的restTemplate是对httpclient进行了封装, 而httpclient是支持池化机制
拓展
对httpclient进行封装的有:Apache的Fluent、es的restHighLevelClient、spring的restTemplate等
上代码:
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
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.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory requestFactory){
return new RestTemplate(requestFactory);
}
@Bean
public ClientHttpRequestFactory httpRequestFactory(){
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
@Bean
public HttpClient httpClient(){
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
//设置连接池最大是500个连接
connectionManager.setMaxTotal(500);
//MaxPerRoute是对maxtotal的细分,每个主机的并发最大是300,route是指域名
connectionManager.setDefaultMaxPerRoute(300);
RequestConfig requestConfig = RequestConfig.custom()
//返回数据的超时时间
.setSocketTimeout(20000)
//连接上服务器的超时时间
.setConnectTimeout(10000)
//从连接池中获取连接的超时时间
.setConnectionRequestTimeout(1000)
.build();
CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.build();
return closeableHttpClient;
}
}
使用:
@Autowired private RestTemplate restTemplate; HttpEntity entity = new HttpEntity<>(headers); ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
问题解决~