HttpClient,RestTemplate超时设置

文章通过一个实例展示了在使用RestTemplate和HttpClient时,如果没有设置超时时间,当目标服务异常如挂起或断网时,请求可能会卡住。通过设置连接超时和读取超时时间,可以避免这种无响应的情况,防止程序卡死。测试中发现,未设置超时时间时,服务器挂掉会导致NoHttpResponseException,而服务器断网或挂起则可能导致SocketTimeoutException。
摘要由CSDN通过智能技术生成

最近在使用RestTemplate的时候发现一个坑,就是某一次发送请求的时候突然卡住不动了,而且没有任何报错提示,重启下服务器就好,可见是因为某些特殊原因导致的,在网上找了下资料发现是因为没有设置超时时间,默认超时时间为永久,就想做一些试验复现下这个bug.

HttpClient试验

不设置HttpClient超时时间,看看发送请求的过程中如果目标服务突然挂掉会怎么样

public class HttpTimeoutTest {
    @Test
    public void testHttpClient() {
        String url = "http://localhost:8081/httpTest/testSleep";
        Map params = new HashMap();
        params.put("time", 30);
        String result = doGet(url, params, null);
        System.out.println(result);
    }

    private static CloseableHttpClient buildClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        //设置连接超时时间
        RequestConfig.Builder builder = RequestConfig.custom();
        /*builder.setConnectTimeout(60 * 1000);
        builder.setSocketTimeout(180 * 1000);*/
        RequestConfig requestConfig = builder.build();
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                return true;
            }
        }).build();
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext,
                NoopHostnameVerifier.INSTANCE);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setSSLSocketFactory(sslSocketFactory).build();
        return httpclient;
    }

    public static String doGet(String url, Map<String, Object> params, Map<String, String> headersMap) {
        String result = null;
        CloseableHttpClient httpClient = null;
        try {
            httpClient = buildClient();
            String apiUrl = url;
            if (null != params && params.size() > 0) {
                StringBuffer param = new StringBuffer();
                int i = 0;
                for (String key : params.keySet()) {
                    if (i == 0)
                        param.append("?");
                    else
                        param.append("&");
                    param.append(key).append("=").append(params.get(key));
                    i++;
                }
                apiUrl += param;
            }

            HttpGet httpGet = new HttpGet(apiUrl);
            if (null != headersMap && headersMap.size() > 0) {
                for (Map.Entry<String, String> entry : headersMap.entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    httpGet.addHeader(new BasicHeader(key, value));
                }
            }
            CloseableHttpResponse response = httpClient.execute(httpGet);
            try {
                HttpEntity entity = response.getEntity();
                if (null != entity) {
                    result = EntityUtils.toString(entity, "utf-8");
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(httpClient);
        }
        return result;
    }
}

发现抛NoHttpResponseException了,目标服务突然断开不会造成卡死

org.apache.http.NoHttpResponseException: The target server failed to respond
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:141)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
    at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
    at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157)

这是为什么呢?

我们知道,建立tcp连接需要三次握手,断开连接需要四次挥手,当进程主动close掉连接时,请求方是可以正常接收到,如果是进程突然挂掉呢?那操作系统就会去关闭连接,所以请求方还是能知道

那如果是服务器突然断网或者服务器挂掉了呢?这时服务器没法主动断开连接,请求方就会一直等待,这时就会造成卡死现象。

我们给HttpClient设置超时时间,再试试

builder.setConnectTimeout(5 * 1000); // 建立三次握手的超时时间
builder.setSocketTimeout(40 * 1000); // 没收到服务器数据的超时时间

可以发现超过40s还没返回,就会抛SocketTimeoutException

java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:170)

connectTimeout是建立连接的时间,如果服务器没挂,进程挂了,则3s就会报Connection refused: connect,否则就要20s,所以最好也设置下

RestTemplate超时时间

接下去我们用RestTemplate重复以上实验,发现会有同样的问题,这是就要设置超时时间了

    @Test
    public void testRestTemplate() {
        RestTemplate restTemplate = restTemplate();
        String url = "http://localhost:8081/httpTest/testSleep?time=30";
        ResponseEntity responseEntity = restTemplate.getForEntity(url, String.class);
        System.out.println(responseEntity.getBody());
    }

    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setRequestFactory(clientHttpRequestFactory());
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
        return restTemplate;
    }

    public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
        try {
            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new org.apache.http.ssl.TrustStrategy() {
                public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                    return true;
                }
            }).build();
            httpClientBuilder.setSSLContext(sslContext);
            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.getSocketFactory())
                    .register("https", sslConnectionSocketFactory).build();// 注册http和https请求
            // 开始设置连接池
            PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            poolingHttpClientConnectionManager.setMaxTotal(500); // 最大连接数500
            poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100); // 同路由并发数100
            httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
            HttpClient httpClient = httpClientBuilder.build();
            HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); // httpClient连接配置
            clientHttpRequestFactory.setConnectTimeout(20 * 1000);              // 连接超时
            clientHttpRequestFactory.setReadTimeout(30 * 1000);                 // 数据读取超时时间
            clientHttpRequestFactory.setConnectionRequestTimeout(20 * 1000);    // 连接不够用的等待时间
            return clientHttpRequestFactory;
        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
            e.printStackTrace();
        }
        return null;
    }
### 回答1: 设置RestTemplate超时时间的方式有多种,你可以使用ClientHttpRequestFactory、HttpComponentsClientHttpRequestFactory和SimpleClientHttpRequestFactory等方式来设置超时时间。 ### 回答2: RestTemplate是Spring框架提供的用于进行HTTP请求的工具类,通过它可以方便地发送各种类型的HTTP请求并获取响应结果。在使用RestTemplate发送HTTP请求时,有时需要设置超时时间来控制请求的最大等待时间,以避免请求因等待时间过长导致的效率低下或系统阻塞。 RestTemplate设置超时时间可以通过以下两种方式进行: 1. HttpComponentsClientHttpRequestFactory方式: ```java // 创建HttpComponentsClientHttpRequestFactory实例 HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); // 设置连接超时时间(单位:毫秒) factory.setConnectTimeout(5000); // 设置读取超时时间(单位:毫秒) factory.setReadTimeout(5000); // 创建RestTemplate实例,并将HttpComponentsClientHttpRequestFactory实例设置为其请求工厂 RestTemplate restTemplate = new RestTemplate(factory); ``` 2. SimpleClientHttpRequestFactory方式: ```java // 创建SimpleClientHttpRequestFactory实例 SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); // 设置连接超时时间(单位:毫秒) factory.setConnectTimeout(5000); // 设置读取超时时间(单位:毫秒) factory.setReadTimeout(5000); // 创建RestTemplate实例,并将SimpleClientHttpRequestFactory实例设置为其请求工厂 RestTemplate restTemplate = new RestTemplate(factory); ``` 上述代码中,使用HttpComponentsClientHttpRequestFactory或SimpleClientHttpRequestFactory创建实例,并通过setConnectTimeout和setReadTimeout方法设置连接超时时间和读取超时时间,单位都是毫秒。然后将创建的请求工厂实例设置RestTemplate实例,从而实现对HTTP请求的超时时间进行设置。 以此设置超时时间后,RestTemplate在发送HTTP请求时,如果请求等待时间超过设置超时时间,将会抛出SocketTimeoutException异常,开发者可以根据需要进行相应的处理。 ### 回答3: 在使用RestTemplate发送HTTP请求时,可以通过设置超时时间来控制请求的响应时间RestTemplate是Spring框架中的一个HTTP请求客户端,它提供了一些用于发送HTTP请求的方法。要设置RestTemplate超时时间,可以通过修改其底层的HttpClient或OkHttpClient实例来实现。下面给出两种方法来设置RestTemplate超时时间。 方法一:通过修改HttpClient实例来设置超时时间。 ```java // 创建HttpComponentsClientHttpRequestFactory实例 HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); // 设置连接超时时间 factory.setConnectTimeout(5000); // 设置读取超时时间 factory.setReadTimeout(5000); // 创建RestTemplate实例,并设置其RequestFactory属性为HttpComponentsClientHttpRequestFactory实例 RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(factory); ``` 方法二:通过修改OkHttpClient实例来设置超时时间。 ```java // 创建OkHttpClient.Builder实例 OkHttpClient.Builder builder = new OkHttpClient.Builder(); // 设置连接超时时间 builder.connectTimeout(5, TimeUnit.SECONDS); // 设置读取超时时间 builder.readTimeout(5, TimeUnit.SECONDS); // 创建OkHttpClient实例 OkHttpClient client = builder.build(); // 创建OkHttp3ClientHttpRequestFactory实例,并设置其OkHttpClient属性为上面创建的OkHttpClient实例 OkHttp3ClientHttpRequestFactory factory = new OkHttp3ClientHttpRequestFactory(client); // 创建RestTemplate实例,并设置其RequestFactory属性为OkHttp3ClientHttpRequestFactory实例 RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(factory); ``` 通过以上两种方法,我们可以根据需要设置RestTemplate的连接超时时间和读取超时时间,来控制请求的响应时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值