许多线程在"等待"使用RestTemplate时

时间:2017-09-25 14:25:48

标签: spring multithreading tomcat spring-boot java-8

当许多请求来到我的网站时,我遇到了一个慢速问题,它开始产生"等待"线程,我已将其余模板设置为Bean

@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    return restTemplateBuilder
            .setConnectTimeout(Integer.parseInt(env.getProperty("service.configuration.http.http-request-timeout")))
            .setReadTimeout(Integer.parseInt(env.getProperty("service.configuration.http.http-request-timeout")))
            .requestFactory(clientHttpRequestFactory())
            .build();
}

当我查找产生该问题的过程时,我发现HttpClient在等待。

有谁知道我该怎么做才能解决这个问题?

我使用 java8,apache tomcat,spring boot

2 个答案:

答案 0 :(得分:0)

在我过去的项目中,我使用了这种配置:

@Bean
public RestTemplate restTemplate()
{
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); 
    factory.setHttpClient(httpClient());
    RestTemplate result = new RestTemplate(factory);
    return result;
}
@Bean
public HttpClient httpClient()
{
    CloseableHttpClient httpClient = null;
    //Use a connection pool
    PoolingHttpClientConnectionManager pcm = new PoolingHttpClientConnectionManager();

    HttpClientBuilder hcb = HttpClientBuilder.create();
    //Close Idle connection after 5 seconds
    pcm.closeIdleConnections(5000, TimeUnit.MILLISECONDS);
    //Specify all the timeouts in milli-seconds
    RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(5000).setSocketTimeout(5000).setConnectTimeout(5000).build();
    hcb.setDefaultRequestConfig(config);
    hcb.setConnectionManager(pcm).setConnectionManagerShared(true);
    // Check if proxy is required to connect to the final resource
    if (proxyEnable)
    {
        //If enabled.... configure it
        BasicCredentialsProvider credentialProvider = new BasicCredentialsProvider();
        AuthScope scope = new AuthScope(hostProxy, portProxy);
        if( StringUtils.hasText(usernameProxy) && StringUtils.hasText(passwordProxy) )
        {

            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(usernameProxy, passwordProxy);
            credentialProvider.setCredentials(scope, credentials);
        }
        hcb.setDefaultCredentialsProvider(credentialProvider).setRoutePlanner(proxyRoutePlanner);
    }
    //Use custom keepalive strategy
    if (cas != null)
    {
        hcb.setKeepAliveStrategy(cas);
    }
    httpClient = hcb.build();
    return httpClient;
}

cas的实例是:

public class WsKeepAliveStrategy implements ConnectionKeepAliveStrategy
    {
        private Long timeout;

        @Override
        public long getKeepAliveDuration(HttpResponse response, HttpContext context)
        {
            return timeout;
        }

        public void setTimeout(Long timeout)
        {
            this.timeout = timeout;
        }

    }

通过这种方式,我可以配置httpclient以使用连接池,指定何时关闭空闲连接并指定套接字超时,连接超时,连接请求超时

通过使用此配置,我不再添加问题

我希望它有用

Angelo

答案 1 :(得分:0)

必须是缺少超时的情况,应尝试获取与您的情况有关的确切问题,并更改引起该问题的设置。将RequestFactory更改为另一个库可能不一定会解决所有问题,因此我的建议是首先识别它。 例如: 我们遇到了类似的问题,线程被卡在restTemplate中,所以我们进行了一个线程转储,就像

    "pool-12-thread-1" #41 prio=5 os_prio=0 tid=0x00007f17a624e000 nid=0x3d runnable [0x00007f1738f96000]
   java.lang.Thread.State: RUNNABLE
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:170)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
    at sun.security.ssl.InputRecord.read(InputRecord.java:503)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
    - locked <0x00000000ebc7d888> (a java.lang.Object)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:930)
    at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
    - locked <0x00000000ebc7d8a0> (a sun.security.ssl.AppInputStream)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
    - locked <0x00000000e94b9608> (a java.io.BufferedInputStream)
    at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:704)
    at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1569)
    - locked <0x00000000d57e5a30> (a sun.net.www.protocol.https.DelegateHttpsURLConnection)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
    - locked <0x00000000d57e5a30> (a sun.net.www.protocol.https.DelegateHttpsURLConnection)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:84)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)

它清楚表明原因是缺少读取超时,因此我们添加了

final SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    requestFactory.setReadTimeout(10_000);  // 10 sec as needed by us
    final RestTemplate restTemplate = new RestTemplate(requestFactory);

获得原因后,类似地,添加适当的超时时间