Java HttpClient中的三种超时设置区别

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

API的描述如下:


 
 
  1. /**
  2. * Returns the timeout in milliseconds used when requesting a connection
  3. * from the connection manager. A timeout value of zero is interpreted
  4. * as an infinite timeout.
  5. * <p>
  6. * A timeout value of zero is interpreted as an infinite timeout.
  7. * A negative value is interpreted as undefined (system default).
  8. * </p>
  9. * <p>
  10. * Default: {@code -1}
  11. * </p>
  12. */
  13. public int getConnectionRequestTimeout () {
  14. return connectionRequestTimeout ;
  15. }
  16. /**
  17. * Determines the timeout in milliseconds until a connection is established.
  18. * A timeout value of zero is interpreted as an infinite timeout.
  19. * <p>
  20. * A timeout value of zero is interpreted as an infinite timeout.
  21. * A negative value is interpreted as undefined (system default).
  22. * </p>
  23. * <p>
  24. * Default: {@code -1}
  25. * </p>
  26. */
  27. public int getConnectTimeout () {
  28. return connectTimeout ;
  29. }
  30. /**
  31. * Defines the socket timeout ({@code SO_TIMEOUT}) in milliseconds,
  32. * which is the timeout for waiting for data or, put differently,
  33. * a maximum period inactivity between two consecutive data packets).
  34. * <p>
  35. * A timeout value of zero is interpreted as an infinite timeout.
  36. * A negative value is interpreted as undefined (system default).
  37. * </p>
  38. * <p>
  39. * Default: {@code -1}
  40. * </p>
  41. */
  42. public int getSocketTimeout () {
  43. return socketTimeout ;
  44. }

正确解读一下

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

1. connectTimeOut:指建立连接的超时时间,比较容易理解
2. connectionRequestTimeOut:指从连接池获取到连接的超时时间,如果是非连接池的话,该参数暂时没有发现有什么用处
3. socketTimeOut:指客户端和服务进行数据交互的时间,是指两者之间如果两个数据包之间的时间大于该时间则认为超时,而不是整个交互的整体时间,比如如果设置1秒超时,如果每隔0.8秒传输一次数据,传输10次,总共8秒,这样是不超时的。而如果任意两个数据包之间的时间超过了1秒,则超时。

代码

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


 
 
  1. package me . nabil . demo . springbootdemo ;
  2. import org.slf4j.Logger ;
  3. import org.slf4j.LoggerFactory ;
  4. import org.springframework.boot.SpringApplication ;
  5. import org.springframework.boot.autoconfigure.EnableAutoConfiguration ;
  6. import org.springframework.stereotype.Controller ;
  7. import org.springframework.web.bind.annotation.RequestMapping ;
  8. import org.springframework.web.bind.annotation.RequestMethod ;
  9. import org.springframework.web.bind.annotation.ResponseBody ;
  10. import javax.servlet.http.HttpServletRequest ;
  11. import javax.servlet.http.HttpServletResponse ;
  12. import java.io.IOException ;
  13. /**
  14. * 超时测试
  15. *
  16. * @author nabilzhang
  17. */
  18. @Controller
  19. @EnableAutoConfiguration
  20. @RequestMapping ( value = { "/test" }, method = { RequestMethod . GET })
  21. public class TimeoutTestController {
  22. private static final Logger logger = LoggerFactory . getLogger ( TimeoutTestController . class );
  23. /**
  24. * main方法
  25. *
  26. * @param args 参数数组
  27. */
  28. public static void main ( String args []) {
  29. SpringApplication . run ( TimeoutTestController . class , args );
  30. }
  31. /**
  32. * 1.测试socketOutTimeout,三秒后返回数据
  33. *
  34. * @return
  35. * @throws InterruptedException
  36. */
  37. @RequestMapping ( value = { "/socket_timeout" }, method = { RequestMethod . GET })
  38. @ResponseBody
  39. String socketTimeout () throws InterruptedException {
  40. logger . info ( "socket_timeout" );
  41. Thread . sleep ( 3000 );
  42. return "socket_timeout" ;
  43. }
  44. /**
  45. * 2.测试socketOutTimeout,每隔0.8秒返回数据
  46. *
  47. * @return
  48. * @throws InterruptedException
  49. */
  50. @RequestMapping ( value = { "/socket_timeout_2" }, method = { RequestMethod . GET })
  51. void socketTimeout2 ( HttpServletResponse response ) throws InterruptedException , IOException {
  52. logger . info ( "socket_timeout_2" );
  53. for ( int i = 0 ; i < 10 ; i ++) {
  54. logger . info ( "{}" , i );
  55. response . getWriter (). println ( "" + i );
  56. response . flushBuffer ();
  57. Thread . sleep ( 800 );
  58. }
  59. }
  60. /**
  61. * 3.测试connectionRequestTimeout用的服务,三秒后返回数据
  62. *
  63. * @param request
  64. * @return
  65. * @throws InterruptedException
  66. */
  67. @RequestMapping ( value = { "/connection_request_timeout" }, method = { RequestMethod . GET })
  68. @ResponseBody
  69. String connectionRequestTimeout ( HttpServletRequest request ) throws InterruptedException {
  70. logger . info ( "{}" , request . getRequestURI ());
  71. Thread . sleep ( 3000 );
  72. return "connectionRequestTimeout" ;
  73. }
  74. }

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


 
 
  1. package me . nabil . demo . springbootdemo ;
  2. import org.apache.http.client.ClientProtocolException ;
  3. import org.apache.http.client.config.RequestConfig ;
  4. import org.apache.http.client.methods.CloseableHttpResponse ;
  5. import org.apache.http.client.methods.HttpGet ;
  6. import org.apache.http.conn.ConnectTimeoutException ;
  7. import org.apache.http.impl.client.CloseableHttpClient ;
  8. import org.apache.http.impl.client.HttpClients ;
  9. import org.apache.http.impl.conn.BasicHttpClientConnectionManager ;
  10. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager ;
  11. import org.apache.http.util.EntityUtils ;
  12. import org.junit.Assert ;
  13. import org.junit.Test ;
  14. import java.io.IOException ;
  15. import java.net.SocketTimeoutException ;
  16. import java.util.Date ;
  17. import java.util.concurrent.ExecutorService ;
  18. import java.util.concurrent.Executors ;
  19. /**
  20. * 测试HttpClient超时参数
  21. *
  22. * @author nabilzhang
  23. */
  24. public class TimeoutTestControllerTest {
  25. /**
  26. * 1.connectionTimeout测试:IP无法链接,链接超时
  27. *
  28. * @throws Exception
  29. */
  30. @Test
  31. public void connectionTimeout () throws Exception {
  32. CloseableHttpClient httpclient = HttpClients . createDefault ();
  33. HttpGet httpGet = new HttpGet ( "http://74.125.203.100" );
  34. RequestConfig requestConfig = RequestConfig . custom (). setConnectionRequestTimeout ( 1000 )
  35. . setSocketTimeout ( 1000 ). setConnectTimeout ( 1000 ). build ();
  36. httpGet . setConfig ( requestConfig );
  37. try {
  38. httpclient . execute ( httpGet );
  39. } catch ( ConnectTimeoutException exception ) {
  40. Assert . assertTrue ( exception . getMessage (). contains ( "connect timed out" ));
  41. }
  42. }
  43. /**
  44. * 2.socketTimeout测试,服务端没有指定时间内任何响应,会超时
  45. *
  46. * @throws Exception
  47. */
  48. @Test
  49. public void socketTimeout () throws Exception {
  50. CloseableHttpClient httpclient = HttpClients . createDefault ();
  51. HttpGet httpGet = new HttpGet ( "http://127.0.0.1:8080/test/socket_timeout" );
  52. RequestConfig requestConfig = RequestConfig . custom ()
  53. . setSocketTimeout ( 1000 ). build ();
  54. httpGet . setConfig ( requestConfig );
  55. try {
  56. httpclient . execute ( httpGet );
  57. } catch ( SocketTimeoutException exception ) {
  58. Assert . assertEquals ( "Read timed out" , exception . getMessage ());
  59. }
  60. }
  61. /**
  62. * 3.socketTimeout测试:服务端隔800ms返回一点数据,不会超时
  63. *
  64. * @throws Exception
  65. */
  66. @Test
  67. public void socketTimeoutNo () {
  68. CloseableHttpClient httpclient = HttpClients . createDefault ();
  69. HttpGet httpGet = new HttpGet ( "http://127.0.0.1:8080/test/socket_timeout_2" );
  70. RequestConfig requestConfig = RequestConfig . custom (). setConnectionRequestTimeout ( 1000 )
  71. . setSocketTimeout ( 1000 ). setConnectTimeout ( 1000 ). build ();
  72. httpGet . setConfig ( requestConfig );
  73. try {
  74. httpclient . execute ( httpGet );
  75. CloseableHttpResponse response = httpclient . execute ( httpGet );
  76. System . out . println ( String . format ( "socketTimeoutNo, %s" , EntityUtils . toString ( response . getEntity ())));
  77. } catch ( Exception e ) {
  78. Assert . fail ( "服务端隔800ms返回一点数据,不会超时" );
  79. }
  80. }
  81. /**
  82. * 4.connectionRequestTimeout测试:指从连接管理器(例如连接池)中拿到连接的超时时间
  83. *
  84. * @throws Exception
  85. */
  86. @Test
  87. public void connectionRequestTimeoutWithPoolingConnectionManager () throws Exception {
  88. PoolingHttpClientConnectionManager conMgr = new PoolingHttpClientConnectionManager ();
  89. conMgr . setMaxTotal ( 2 );
  90. final CloseableHttpClient httpclient = HttpClients . custom (). setConnectionManager ( conMgr ). build ();
  91. final HttpGet httpGet = new HttpGet ( "http://127.0.0.1:8080/test/connection_request_timeout" );
  92. RequestConfig requestConfig = RequestConfig . custom (). setConnectTimeout ( 1000 )
  93. . setConnectionRequestTimeout ( 1000 ). setSocketTimeout ( 1000 ). build ();
  94. httpGet . setConfig ( requestConfig );
  95. // 如下多线程占满连接池
  96. ExecutorService executorService = Executors . newFixedThreadPool ( 10 );
  97. for ( int i = 0 ; i < 10 ; i ++) {
  98. executorService . submit ( new Runnable () {
  99. @Override
  100. public void run () {
  101. try {
  102. CloseableHttpResponse response = httpclient . execute ( httpGet );
  103. System . out . println ( String . format ( "connectionRequestTimeoutTest: %s" ,
  104. EntityUtils . toString ( response . getEntity ())));
  105. } catch ( SocketTimeoutException exception ) {
  106. System . out . println ( exception . getMessage ());
  107. } catch ( ClientProtocolException e ) {
  108. e . printStackTrace ();
  109. } catch ( IOException e ) {
  110. e . printStackTrace ();
  111. }
  112. }
  113. });
  114. }
  115. // 在连接池占满的情况下,拿不到就会抛异常
  116. try {
  117. CloseableHttpResponse response = httpclient . execute ( httpGet );
  118. System . out . println ( String . format ( "connectionRequestTimeoutTest: %s" ,
  119. EntityUtils . toString ( response . getEntity ())));
  120. Assert . fail ();
  121. } catch ( Exception exception ) {
  122. // 异常是从连接池拿到连接超时
  123. Assert . assertEquals ( "Timeout waiting for connection from pool" , exception . getMessage ());
  124. System . out . println ( exception . getMessage ());
  125. }
  126. }
  127. /**
  128. * 5.connectionRequestTimeout测试,指从连接管理器中拿到连接的超时时间,由于使用基本的连接管理器,链接被占用时,直接无法分配链接
  129. * connectionRequestTimeout并未生效,目前看来该参数只在连接池奏效.
  130. * 该链接管理器(BasicHttpClientConnectionManager)是单线程情况下可以使用,多线程情况下不要使用。
  131. *
  132. * @throws Exception
  133. */
  134. @Test
  135. public void connectionRequestTimeoutWithBasicConnectionManager () throws Exception {
  136. BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager ();
  137. final CloseableHttpClient httpclient = HttpClients . custom ()
  138. . setConnectionManager ( connManager ). setMaxConnPerRoute ( 1 ). build ();
  139. final HttpGet httpGet = new HttpGet ( "http://127.0.0.1:8080/test/connection_request_timeout" );
  140. RequestConfig requestConfig = RequestConfig . custom (). setConnectTimeout ( 100000 )
  141. . setConnectionRequestTimeout ( 1000000 ). setSocketTimeout ( 1000000 ). build ();
  142. httpGet . setConfig ( requestConfig );
  143. // 如下多线程占满连接
  144. ExecutorService executorService = Executors . newFixedThreadPool ( 10 );
  145. for ( int i = 0 ; i < 10 ; i ++) {
  146. executorService . submit ( new Runnable () {
  147. @Override
  148. public void run () {
  149. CloseableHttpResponse response = null ;
  150. try {
  151. response = httpclient . execute ( httpGet );
  152. System . out . println ( String . format ( "connectionRequestTimeoutTest: %s" ,
  153. EntityUtils . toString ( response . getEntity ())));
  154. } catch ( Exception exception ) {
  155. exception . printStackTrace ();
  156. } finally {
  157. if ( response != null ) {
  158. try {
  159. response . close ();
  160. httpclient . close ();
  161. } catch ( IOException e ) {
  162. e . printStackTrace ();
  163. }
  164. }
  165. }
  166. }
  167. });
  168. }
  169. System . out . println ( new Date ());
  170. // 在连接池占满的情况下,拿不到就会抛异常
  171. try {
  172. CloseableHttpResponse response = httpclient . execute ( httpGet );
  173. System . out . println ( String . format ( "connectionRequestTimeoutTest: %s" ,
  174. EntityUtils . toString ( response . getEntity ())));
  175. Assert . fail ();
  176. } catch ( Exception exception ) {
  177. System . out . println ( new Date ());
  178. exception . printStackTrace ();
  179. // 异常是从连接池拿到连接超时
  180. Assert . assertEquals ( "Connection is still allocated" , exception . getMessage ());
  181. System . out . println ( exception . getMessage ());
  182. }
  183. }
  184. }

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


 
 
  1. <dependency>
  2. <groupId>org.apache.httpcomponents </groupId>
  3. <artifactId>httpclient </artifactId>
  4. <version>4.5.2 </version>
  5. <type>jar </type>
  6. </dependency>

更深理解可以参照:http://www.baeldung.com/httpclient-connection-management

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用Java HttpClient发送Http请求时,我们需要设置超时时间,以防止请求处理过程过长而导致请求无法完成。接下来,我们将介绍如何设置Java HttpClient超时时间。 在Java HttpClient超时时间设置包括连接超时时间和读取超时时间。 连接超时时间的是连接到远程服务器的时间,如果连接到远程服务器的时间超过设定的时间上限,则HttpClient会抛出ConnectTimeoutException异常。 读取超时时间的是从远程服务器读取数据的时间,如果读取数据的时间超过设定的时间上限,则HttpClient会抛出SocketTimeoutException异常。 HttpClient提供了两种方法来设置超时时间: 1. 通过HttpParams对象: ``` HttpParams httpParams = new BasicHttpParams(); //设置连接超时时间 HttpConnectionParams.setConnectionTimeout(httpParams, connectionTimeout); //设置读取数据超时时间 HttpConnectionParams.setSoTimeout(httpParams, soTimeout); //创建HttpClient对象 HttpClient httpClient = new DefaultHttpClient(httpParams); ``` 2. 通过builder模式: ``` //设置连接超时时间 RequestConfig config = RequestConfig.custom() .setConnectTimeout(connectionTimeout) .setSocketTimeout(soTimeout) .build(); //创建HttpClient对象 HttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(config).build(); ``` 总的来说,无论使用哪种方式,我们都需要设置连接超时时间和读取超时时间。适当的超时时间可以提高应用程序的性能和稳定性,减少不必要的等待和阻塞。同时,我们也需要根据实际需求进行调整,以确保在连接和读取过程不会出现超时异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值