tomcat 使用RestTemplate时,许多线程处于“等待”状态

nhn9ugyo  于 2022-11-30  发布在  其他
关注(0)|答案(3)|浏览(288)

我有一个缓慢的问题,当许多请求来到我的网站,它开始生成“等待”线程,我已经设置了休息模板作为一个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 **

8hhllhi2

8hhllhi21#

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

@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以使用连接池,指定何时关闭空闲连接,并指定套接字超时、连接超时、连接请求超时
通过使用此配置,我不会再增加任何问题
我希望它能有用
安杰洛

w51jfk4q

w51jfk4q2#

一定是缺少超时的情况,应该尝试得到在您的情况下发生的确切问题,并更改导致该问题的设置。将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);

同样,在你得到原因后,添加适当的超时

hm2xizp9

hm2xizp93#

HERE解释了发生这种情况的原因。
这个配置与Baeldung的另一篇关于rest template builder的文章是内联的。它看起来很好很干净,但是它隐藏了一个默认的PoolingHttpClientConnectionManager,其中defaultMaxPerRoute设置为5。这个默认的每个路由的最大值是什么意思?它意味着只有5个HTTP连接可以同时连接到同一个主机。
因此,您可以配置RestTemplate以使用池化实现,如HttpComponentsClientHttpRequestFactory,并覆盖defaultMaxPerRoute

PoolingHttpClientConnectionManager poolingConnManager = new 
PoolingHttpClientConnectionManager();
poolingConnManager.setMaxTotal(50);
poolingConnManager.setDefaultMaxPerRoute(50);

相关问题