团队您好:我有一个关于在 Spring Boot 3 应用程序中使用 Apache HttpClient 5 CloseableHttpClient 的问题。我正在执行以下代码:
try
{
final HttpGet httpGet = new HttpGet("http://httpbin.org/get");
httpGet.addHeader("content-type", "application/json");
HttpClientResponseHandler<String> responseHandler = new HttpClientResponseHandler<String>()
{
@Override
public String handleResponse(ClassicHttpResponse _httpResponse) throws HttpException, IOException
{
int status = _httpResponse.getCode();
if (status >= 200 && status < 300)
{
HttpEntity entity = _httpResponse.getEntity();
String stringEntity = EntityUtils.toString(entity);
return entity != null ? stringEntity : null;
}
else
{
throw new ClientProtocolException("Unexpected response status: " + status);
}
}
};
responseBody = closeableHttpClient.execute(httpGet, responseHandler);
log.info("Response Body: " + responseBody);
}
catch (NoSuchElementException _e)
{
log.error("NoSuchElementException: "+ _e.getMessage());
_e.printStackTrace();
}
catch (ClientProtocolException _e)
{
log.error("ClientProtocolException: "+ _e.getMessage());
}
catch (IOException _e)
{
log.error("IOException: "+ _e.getMessage());
}
catch (Exception _e)
{
log.error("Exception: "+ _e.getMessage());
}
但是当我执行这段代码时,出现以下异常:
NoSuchElementException:没有更多可用的标题元素 java.util.NoSuchElementException:没有更多可用的标头元素
要获取 CloseableHttpClient,我有以下配置:
@Slf4j
@Configuration
@EnableScheduling
public class HttpConfig
@Bean
@ConditionalOnMissingBean
public CloseableHttpClient httpClient(HttpClientConnectionManager connectionManager)
{
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(Timeout.ofMilliseconds(15000))
.setResponseTimeout(Timeout.ofMilliseconds(15000))
.build();
return HttpClients.custom()
.setConnectionManager(connectionManager)
.setKeepAliveStrategy(getConnectionKeepAliveStrategy())
.setDefaultRequestConfig(requestConfig)
.build();
}
@Bean(destroyMethod = "")
@ConditionalOnMissingBean
public HttpClientConnectionManager httpClientConnectionManager()
{
HttpHost host1 = new HttpHost("httpbin.org/get", 80);
final PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setConnectionFactory(getHttpConnectionFactory())
.setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
.setConnPoolPolicy(PoolReusePolicy.LIFO)
.build();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(20);
connectionManager.setMaxPerRoute(new HttpRoute(host1), 10);
return connectionManager;
}
public ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy()
{
final ConnectionKeepAliveStrategy connectionKeepAliveStrategy = new ConnectionKeepAliveStrategy()
{
@Override
public TimeValue getKeepAliveDuration(HttpResponse response, HttpContext context)
{
Args.notNull(response, "HTTP response");
final Iterator<HeaderElement> iterator = MessageSupport.iterate(response, HeaderElements.KEEP_ALIVE);
final HeaderElement headerElement = iterator.next();
final String param = headerElement.getName();
final String value = headerElement.getValue();
if (value != null && param.equalsIgnoreCase("timeout"))
{
try
{
return TimeValue.ofSeconds(Long.parseLong(value));
}
catch (final NumberFormatException e)
{
log.error("Connection Keep-Alive Strategy: Number Format Exception: Safely Ignore: " + e.getMessage());
}
}
return TimeValue.ofSeconds(5);
}
};
return connectionKeepAliveStrategy;
}
@Bean
public Runnable getIdleConnectionMonitor(final PoolingHttpClientConnectionManager connectionManager)
{
return new Runnable()
{
@Override
@Scheduled(fixedDelay = 15000)
public void run()
{
try
{
if (connectionManager != null)
{
connectionManager.closeExpired();
connectionManager.closeIdle(Timeout.ofSeconds(60000));
}
}
catch (Exception e)
{
log.error("IdleConnectionMonitor - Exception occurred: Message = {}, Exception = {}", e.getMessage(), e);
}
}
};
}
我不知道这里出了什么问题。任何帮助将不胜感激。
错误消息来自上面代码中此行
final HeaderElement headerElement = iterator.next();
处的 ConnectionKeepAliveStrategy 配置。
在 Spring 6.X 和 Spring Boot 3 中,org.apache.httpclient 更新为 org.apache.httpcomponents.client5:httpclient5,并且 httpclient5 生成的响应没有 HeaderElement,但标头是响应的一部分目的。因此,将实现更改为此将起作用。我已经在 Spring Boot 3.2.5 中的 ConnectiveKeepAliveStrategy 下面对此进行了测试
return new ConnectionKeepAliveStrategy() {
@Override
public TimeValue getKeepAliveDuration(HttpResponse httpResponse, HttpContext httpContext) {
if(httpResponse.containsHeader(HeaderElements.KEEP_ALIVE)) {
String keepAliveHeader = httpResponse.getFirstHeader(HeaderElements.KEEP_ALIVE).getValue();
String timeoutValue = keepAliveHeader.replaceAll("[^0-9]", "");
if(!timeoutValue.isEmpty()) {
return TimeValue.ofMilliseconds(Long.parseLong(timeoutValue) * 1000);
}
}
return TimeValue.ofSeconds(3000);
}
};
希望这有帮助!