HttpClient中的三种超时设置区别

最近项目中在使用HttpClient有三个超时(connectionRequestTimeout,connectTimeout,socketTimeout)时间理解得不是很透彻,API文档不是很理解,专门写Demo理解了一下。

API的描述如下:

/**
     * Returns the timeout in milliseconds used when requesting a connection
     * from the connection manager. A timeout value of zero is interpreted
     * as an infinite timeout.
     * <p>
     * A timeout value of zero is interpreted as an infinite timeout.
     * A negative value is interpreted as undefined (system default).
     * </p>
     * <p>
     * Default: {@code -1}
     * </p>
     */
    public int getConnectionRequestTimeout() {
        return connectionRequestTimeout;
    }
 
    /**
     * Determines the timeout in milliseconds until a connection is established.
     * A timeout value of zero is interpreted as an infinite timeout.
     * <p>
     * A timeout value of zero is interpreted as an infinite timeout.
     * A negative value is interpreted as undefined (system default).
     * </p>
     * <p>
     * Default: {@code -1}
     * </p>
     */
    public int getConnectTimeout() {
        return connectTimeout;
    }
 
    /**
     * Defines the socket timeout ({@code SO_TIMEOUT}) in milliseconds,
     * which is the timeout for waiting for data  or, put differently,
     * a maximum period inactivity between two consecutive data packets).
     * <p>
     * A timeout value of zero is interpreted as an infinite timeout.
     * A negative value is interpreted as undefined (system default).
     * </p>
     * <p>
     * Default: {@code -1}
     * </p>
     */
    public int getSocketTimeout() {
        return socketTimeout;
    }

正确解读

API中不能看出正式的含义是什么,经过demo之后,终于知道了各自含义

1. connectTimeOut:指建立连接的超时时间,比较容易理解

2. connectionRequestTimeOut:指从连接池获取到连接的超时时间,如果是非连接池的话,该参数暂时没有发现有什么用处

3. socketTimeOut:指客户端和服务进行数据交互的时间,是指两者之间如果两个数据包之间的时间大于该时间则认为超时,而不是整个交互的整体时间,比如如果设置1秒超时,如果每隔0.8秒传输一次数据,传输10次,总共8秒,这样是不超时的。而如果任意两个数据包之间的时间超过了1秒,则超时。

 测试代码

首先是为这次demo写的服务代码,包含几个controller方法(Spring MVC)

package me.nabil.demo.springbootdemo;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
 
/**
 * 超时测试
 *
 * @author nabilzhang
 */
@Controller
@EnableAutoConfiguration
@RequestMapping(value = {"/test"}, method = {RequestMethod.GET})
public class TimeoutTestController {
 
    private static final Logger logger = LoggerFactory.getLogger(TimeoutTestController.class);
 
    /**
     * main方法
     *
     * @param args 参数数组
     */
    public static void main(String args[]) {
        SpringApplication.run(TimeoutTestController.class, args);
    }
 
    /**
     * 1.测试socketOutTimeout,三秒后返回数据
     *
     * @return
     * @throws InterruptedException
     */
    @RequestMapping(value = {"/socket_timeout"}, method = {RequestMethod.GET})
    @ResponseBody
    String socketTimeout() throws InterruptedException {
        logger.info("socket_timeout");
        Thread.sleep(3000);
        return "socket_timeout";
    }
 
    /**
     * 2.测试socketOutTimeout,每隔0.8秒返回数据
     *
     * @return
     * @throws InterruptedException
     */
    @RequestMapping(value = {"/socket_timeout_2"}, method = {RequestMethod.GET})
    void socketTimeout2(HttpServletResponse response) throws InterruptedException, IOException {
        logger.info("socket_timeout_2");
        for (int i = 0; i < 10; i++) {
            logger.info("{}", i);
            response.getWriter().println("" + i);
            response.flushBuffer();
            Thread.sleep(800);
        }
    }
 
    /**
     * 3.测试connectionRequestTimeout用的服务,三秒后返回数据
     *
     * @param request
     * @return
     * @throws InterruptedException
     */
    @RequestMapping(value = {"/connection_request_timeout"}, method = {RequestMethod.GET})
    @ResponseBody
    String connectionRequestTimeout(HttpServletRequest request) throws InterruptedException {
        logger.info("{}", request.getRequestURI());
        Thread.sleep(3000);
        return "connectionRequestTimeout";
    }
}

如下是客户端的测试Case,下面几个Case分别测试了几个超时时间的各种情况,Case全部通过才可以

package me.nabil.demo.springbootdemo;
 
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.junit.Assert;
import org.junit.Test;
 
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * 测试HttpClient超时参数
 *
 * @author nabilzhang
 */
public class TimeoutTestControllerTest {
 
    /**
     * 1.connectionTimeout测试:IP无法链接,链接超时
     * @throws Exception
     */
    @Test
    public void connectionTimeout() throws Exception {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://74.125.203.100");
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(1000)
                .setSocketTimeout(1000).setConnectTimeout(1000).build();
        httpGet.setConfig(requestConfig);
        try {
            httpclient.execute(httpGet);
        } catch (ConnectTimeoutException exception) {
            Assert.assertTrue(exception.getMessage().contains("connect timed out"));
        }
 
    }
 
    /**
     * 2.socketTimeout测试,服务端没有指定时间内任何响应,会超时
     * @throws Exception
     */
    @Test
    public void socketTimeout() throws Exception {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://127.0.0.1:8080/test/socket_timeout");
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(1000).build();
        httpGet.setConfig(requestConfig);
        try {
            httpclient.execute(httpGet);
        } catch (SocketTimeoutException exception) {
            Assert.assertEquals("Read timed out", exception.getMessage());
        }
    }
 
    /**
     * 3.socketTimeout测试:服务端隔800ms返回一点数据,不会超时
     * @throws Exception
     */
    @Test
    public void socketTimeoutNo() {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://127.0.0.1:8080/test/socket_timeout_2");
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(1000)
                .setSocketTimeout(1000).setConnectTimeout(1000).build();
        httpGet.setConfig(requestConfig);
 
        try {
            httpclient.execute(httpGet);
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println(String.format("socketTimeoutNo, %s", EntityUtils.toString(response.getEntity())));
        } catch (Exception e) {
            Assert.fail("服务端隔800ms返回一点数据,不会超时");
        }
 
 
    }
 
    /**
     * 4.connectionRequestTimeout测试:指从连接管理器(例如连接池)中拿到连接的超时时间
     * @throws Exception
     */
    @Test
    public void connectionRequestTimeoutWithPoolingConnectionManager() throws Exception {
        PoolingHttpClientConnectionManager conMgr = new PoolingHttpClientConnectionManager();
        conMgr.setMaxTotal(2);
 
        final CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(conMgr).build();
        final HttpGet httpGet = new HttpGet("http://127.0.0.1:8080/test/connection_request_timeout");
 
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(1000)
                .setConnectionRequestTimeout(1000).setSocketTimeout(1000).build();
        httpGet.setConfig(requestConfig);
 
        // 如下多线程占满连接池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        CloseableHttpResponse response = httpclient.execute(httpGet);
                        System.out.println(String.format("connectionRequestTimeoutTest: %s",
                                EntityUtils.toString(response.getEntity())));
                    } catch (SocketTimeoutException exception) {
                        System.out.println(exception.getMessage());
                    } catch (ClientProtocolException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
 
        // 在连接池占满的情况下,拿不到就会抛异常
        try {
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println(String.format("connectionRequestTimeoutTest: %s",
                    EntityUtils.toString(response.getEntity())));
            Assert.fail();
        } catch (Exception exception) {
            // 异常是从连接池拿到连接超时
            Assert.assertEquals("Timeout waiting for connection from pool", exception.getMessage());
            System.out.println(exception.getMessage());
        }
 
    }
 
    /**
     * 5.connectionRequestTimeout测试,指从连接管理器中拿到连接的超时时间,由于使用基本的连接管理器,链接被占用时,直接无法分配链接
     * connectionRequestTimeout并未生效,目前看来该参数只在连接池奏效.
     * 该链接管理器(BasicHttpClientConnectionManager)是单线程情况下可以使用,多线程情况下不要使用。
     * @throws Exception
     */
    @Test
    public void connectionRequestTimeoutWithBasicConnectionManager() throws Exception {
 
        BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager();
        final CloseableHttpClient httpclient = HttpClients.custom()
                .setConnectionManager(connManager).setMaxConnPerRoute(1).build();
        final HttpGet httpGet = new HttpGet("http://127.0.0.1:8080/test/connection_request_timeout");
 
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(100000)
                .setConnectionRequestTimeout(1000000).setSocketTimeout(1000000).build();
        httpGet.setConfig(requestConfig);
 
        // 如下多线程占满连接
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    CloseableHttpResponse response = null;
                    try {
                        response = httpclient.execute(httpGet);
                        System.out.println(String.format("connectionRequestTimeoutTest: %s",
                                EntityUtils.toString(response.getEntity())));
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    } finally {
                        if (response != null) {
                            try {
                                response.close();
                                httpclient.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
 
                    }
                }
            });
        }
        System.out.println(new Date());
        // 在连接池占满的情况下,拿不到就会抛异常
        try {
            CloseableHttpResponse response = httpclient.execute(httpGet);
            System.out.println(String.format("connectionRequestTimeoutTest: %s",
                    EntityUtils.toString(response.getEntity())));
            Assert.fail();
        } catch (Exception exception) {
            System.out.println(new Date());
            exception.printStackTrace();
            // 异常是从连接池拿到连接超时
            Assert.assertEquals("Connection is still allocated", exception.getMessage());
            System.out.println(exception.getMessage());
        }
 
    }
 
}

注:上面Case是使用httpclient版本4.5.2测试所得。

<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
            <type>jar</type>
 </dependency>

----------------------------------------------------------------------------

原文:https://blog.csdn.net/zmx729618/article/details/78361238

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值