Я создал загрузочное приложение Spring, которое вызывает REST API с помощью Rest Template. Сначала я использовал только компонент Rest Template, и время отклика дельты между основным приложением и оболочкой во время теста производительности составляло всего около 5-10 мс.
Чтобы иметь больше контроля над параллелизмом и HTTP соединений мы решили использовать httpclient-4.5.10 jar
и использовали CloseableHttpClient
для создания компонента Rest Template Bean. Тем не менее, после реализации этого мы заметили всплеск в среднем. время реакции дельты более 82 мс После тщательного анализа мы выяснили, что CommonsHttp[app-name]: execute
занимает около 81,4 мс. Это означает, что HTTP-клиент отвечает за более высокое время отклика. Мы попробовали несколько способов настройки производительности, изменив стратегию поддержки активности, количество потоков и т. Д. c., Но ни один из них, похоже, не работает.
Вот конфигурация клиента Http:
// Determines the timeout in milliseconds until a connection is established.
@Value("${http-client.connect-timeout}")
private int connectTimeout;
// The timeout when requesting a connection from the connection manager.
@Value("${http-client.request-timeout}")
private int requestTimeout;
// The timeout for waiting for data
@Value("${http-client.socket-timeout}")
private int socketTimeout;
@Value("${http-client.max-total-connections}")
private int maxTotalConnections;
@Value("${http-client.default-max-per-route}")
private int defaultMaxPerRoute;
@Value("${http-client.default-keep-alive-time}")
private int defaultKeepAliveTime;
@Value("${http-client.close-idle-connection-wait-time}")
private int closeIdleConnectionWaitTime;
@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());
}
SSLConnectionSocketFactory sslsf = null;
try {
sslsf = new SSLConnectionSocketFactory(builder.build());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
log.error("Pooling Connection Manager Initialisation failure because of {}", e.getMessage());
}
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory>create().register("https", sslsf)
.register("http", new PlainConnectionSocketFactory())
.build();
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolingConnectionManager.setMaxTotal(maxTotalConnections);
poolingConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
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 defaultKeepAliveTime;
}
};
}
@Bean
public CloseableHttpClient httpClient() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(requestTimeout)
.setConnectTimeout(connectTimeout)
.setSocketTimeout(socketTimeout).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(closeIdleConnectionWaitTime, TimeUnit.MILLISECONDS);
} else {
log.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised");
}
} catch (Exception e) {
log.error("run IdleConnectionMonitor - Exception occurred. msg={}, e={}", e.getMessage(), e);
}
}
};
}
Конфигурация шаблона отдыха:
@Autowired
CloseableHttpClient httpClient;
@Bean
public RestTemplate getRestTemplate(RestTemplateRequestResponseLoggingInterceptor loggingInterceptor) {
ClientHttpRequestFactory factory = new BufferingClientHttpRequestFactory(clientHttpRequestFactory());
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setInterceptors(Collections.singletonList(loggingInterceptor));
return restTemplate;
}
@Bean
public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setHttpClient(httpClient);
return clientHttpRequestFactory;
}
Свойства приложения:
# HTTP Client configs
http-client.connect-timeout: 2000
http-client.request-timeout: 5000
http-client.socket-timeout: 5000
http-client.max-total-connections: 200
http-client.default-max-per-route: 50
http-client.default-keep-alive-time: 300000
http-client.close-idle-connection-wait-time: 480000