Spring Boot Rest模板使连接保持活动状态

时间:2020-06-22 15:01:08

标签: spring spring-boot httpclient okhttp resttemplate

我有一个Spring Boot应用程序,正在创建对外部系统的请求。一段时间(3-4分钟)后,外部系统响应。在收到远程API的响应之前,我想保持连接打开状态。我尝试使用webflux,尝试在application.yml文件中为我的应用程序设置连接超时。我可以让应用程序等待响应超过2分钟。我尝试过的一些代码

@Configuration
public class RestConfigurations {
@Bean(name = "restTemplate")
public RestTemplate getRestTemplate(RestTemplateBuilder restTemplateBuilder) {
    RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

    // Error handler
    restTemplate.setErrorHandler(new CustomRestTemplateErrorHandler());

    // Media converters
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setSupportedMediaTypes(Arrays.asList(
            MediaType.APPLICATION_JSON,
            MediaType.TEXT_PLAIN,
            MediaType.TEXT_HTML));
    restTemplate.getMessageConverters().add(converter);

    return restTemplate;
}

private ClientHttpRequestFactory getClientHttpRequestFactory() {
    int connectionTimeout = 15*60000; // milliseconds
    int socketTimeout = 15*60000; // milliseconds
    RequestConfig config = RequestConfig.custom()
            .setConnectTimeout(connectionTimeout)
            .setConnectionRequestTimeout(connectionTimeout)
            .setSocketTimeout(socketTimeout)
            .build();
    CloseableHttpClient client = HttpClientBuilder
            .create()
            .setDefaultRequestConfig(config)
            .setKeepAliveStrategy(connectionKeepAliveStrategy())
            .build();
    return new HttpComponentsClientHttpRequestFactory(client);
}

public HttpComponentsClientHttpRequestFactory getHttpClientFactory() {
    HttpComponentsClientHttpRequestFactory httpClientFactory = new 
   HttpComponentsClientHttpRequestFactory(
            HttpClients.createDefault()
    );
    httpClientFactory.setConnectTimeout(15 * 600000);
    httpClientFactory.setReadTimeout(15 * 600000);
    httpClientFactory.setConnectionRequestTimeout(15 * 600000);
    return httpClientFactory;
}

public ClientHttpRequestFactory createRequestFactory(){
    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
    connectionManager.setMaxTotal(400);
    connectionManager.setDefaultMaxPerRoute(200);
    RequestConfig requestConfig = RequestConfig
            .custom()
            .setConnectionRequestTimeout(5000)
            .setSocketTimeout(10000)
            .build();
    CloseableHttpClient httpClient = HttpClients
            .custom()
            .setConnectionManager(connectionManager)
            .setKeepAliveStrategy(connectionKeepAliveStrategy())
            .setDefaultRequestConfig(requestConfig)
            .build();
    return new HttpComponentsClientHttpRequestFactory(httpClient);
}


private ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
    return (response,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")){
                try {
                    return Long.parseLong(value) * 999999999;
                } catch (NumberFormatException exception) {
                    exception.printStackTrace();
                }
            }
        }

        return 15 * 60000;
    } ;
}

@Bean(name = "webClient")
public WebClient webClient() {
    TcpClient tcpClient = TcpClient
            .create()
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000 * 15)//15 minutes
            .doOnConnected(connection -> {
                connection.addHandlerLast(new ReadTimeoutHandler(15, TimeUnit.MINUTES));
                connection.addHandlerLast(new WriteTimeoutHandler(15, TimeUnit.MINUTES));
            });

    return WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient).wiretap(true)))
            .build();
}

此配置均无效。我尝试像这样使用okhttpclient。

public class OkHttpClientFactoryImpl implements OkHttpClientFactory {
  @Override 
  public OkHttpClient.Builder createBuilder(boolean disableSslValidation) {
     OkHttpClient.Builder builder = new OkHttpClient.Builder();
     ConnectionPool okHttpConnectionPool = new ConnectionPool(50, 30, TimeUnit.SECONDS);
     builder.connectionPool(okHttpConnectionPool);
    builder.connectTimeout(20, TimeUnit.MINUTES);
    builder.readTimeout(20, TimeUnit.MINUTES);
    builder.writeTimeout(20, TimeUnit.MINUTES);
    builder.retryOnConnectionFailure(false);
    return builder;
   }
 }

@Bean
@Qualifier("OKSpringCommonsRestTemplate")
public ClientHttpRequestFactory createOKCommonsRequestFactory() {
    OkHttpClientFactoryImpl httpClientFactory = new OkHttpClientFactoryImpl();
    OkHttpClient client = httpClientFactory.createBuilder(false).build();
    return new OkHttp3ClientHttpRequestFactory(client);
}

它没有用。除了我已设置的内容外,我不知道什么可能导致连接关闭。

收到答案后,我尝试了:

HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .timeout(Duration.ofMinutes(10))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(json))
            .build();

    HttpClient client= HttpClient.newHttpClient();
    try {
        client.send(request, HttpResponse.BodyHandlers.ofString());
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    }

结果是相同的。 2分钟后,我收到一条错误消息,提示java.io.IOException:HTTP / 1.1标头解析器未收到任何字节

2 个答案:

答案 0 :(得分:0)

我发现了问题。实际上是由于服务器在呼叫的另一端。 Express服务器默认在2分钟后关闭连接。

答案 1 :(得分:0)

增加服务器/ LB上的客户端空闲超时数量。