今天开始介绍下Apache HttpClient 4的全面指南,从基础到高级进阶,本文是基础知识一。
发送Http请求后 - 我们返回一个 org.apache.http.HttpResponse
实例,我们可以获取到响应的状态行,并隐式地访问状态代码:
response.getStatusLine().getStatusCode() 复制代码
使用它,我们可以验证从服务器收到的代码是否是正确的:
@Test public void test() throws ClientProtocolException, IOException { HttpClient client = HttpClientBuilder.create().build(); HttpResponse response = client.execute(new HttpGet(SAMPLE_URL)); int statusCode = response.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); } 复制代码
这里面,使用 org.apache.http.HttpStatus
中库中提供的预定义状态代码。
HttpClient可以配置超时的时间,接下来,我们看下如何使用。
HttpClient的自带了大量的配置参数,而所有的这些都可以在一个通用的Map集合中来设置。
有3个超时参数配置:
DefaultHttpClient httpClient = new DefaultHttpClient(); int timeout = 5; // seconds HttpParams httpParams = httpClient.getParams(); httpParams.setParameter( CoreConnectionPNames.CONNECTION_TIMEOUT, timeout * 1000); httpParams.setParameter( CoreConnectionPNames.SO_TIMEOUT, timeout * 1000); httpParams.setParameter( ClientPNames.CONN_MANAGER_TIMEOUT, new Long(timeout * 1000)); 复制代码
这些参数中更重要的 - 即前两个 - 也可以通过更安全的API进行设置:
DefaultHttpClient httpClient = new DefaultHttpClient(); int timeout = 5; // seconds HttpParams httpParams = httpClient.getParams(); HttpConnectionParams.setConnectionTimeout( httpParams, timeout * 1000); // http.connection.timeout HttpConnectionParams.setSoTimeout( httpParams, timeout * 1000); // http.socket.timeout 复制代码
第三个参数在HttpConnectionParams中没有自定义setter ,它仍然需要通过setParameter方法手动设置。
4.3中引入新API来设置超时的方法,这个更方便快捷:
int timeout = 5; RequestConfig config = RequestConfig.custom() .setConnectTimeout(timeout * 1000) .setConnectionRequestTimeout(timeout * 1000) .setSocketTimeout(timeout * 1000).build(); CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build(); 复制代码
这是以类型安全且可读的方式配置所有三个超时的推荐方法。
现在,让我们解释一下这些不同类型的超时意味着什么:
前两个参数,connection和socket超时是最重要的。但是,在高负载情况下设置获取连接的超时非常重要,这就是不应忽略第三个参数的原因。
虽然设置建立HTTP连接和不接收数据的超时非常有用,但有时我们需要为整个请求设置强制超时。
例如,可能大型文件的下载符合此类别。在这种情况下,可以成功建立连接,数据可以一直通过,但是我们仍然需要确保操作不会超过某个特定时间阈值。
HttpClient没有任何允许我们为请求设置总超时的配置; 但是,它确实为请求提供了中止功能,因此我们可以利用该机制实现一个简单的超时机制:
HttpGet getMethod =new HttpGet("http://localhost:8080/"); int hardTimeout = 5; // seconds TimerTask task = new TimerTask() { @Override public void run() { if (getMethod != null) { getMethod.abort(); } } }; new Timer(true).schedule(task, hardTimeout * 1000); HttpResponse response = httpClient.execute(getMethod); System.out.println( "HTTP Status of response: " + response.getStatusLine().getStatusCode()); 复制代码
我们正在利用 java.util.Timer
和 java.util.TimerTask
来设置一个简单的延迟任务,该任务在5秒强制超时后中止HTTP GET请求。
一些较大的域名将使用DNS循环配置是很常见的,基本上具有映射到多个IP地址的相同域名。这引发了针对此类域的超时的新挑战,原因很简单,因为HttpClient将尝试连接到超时的域:
HttpClient获取到该域的IP路由列表 它尝试第一个 - 超时 它尝试第二个 - 也超时 等等 … 因此,正如你所看到的 - 当我们期望它时,整体操作不会超时。相反 - 当所有可能的路线超时时它会超时。更重要的是 - 这将完全透明地发生在客户端上(除非你在DEBUG级别配置了日志)。
这是一个可以运行并复制此问题的简单示例:
int timeout = 3; RequestConfig config = RequestConfig.custom(). setConnectTimeout(timeout * 1000). setConnectionRequestTimeout(timeout * 1000). setSocketTimeout(timeout * 1000).build(); CloseableHttpClient client = HttpClientBuilder.create() .setDefaultRequestConfig(config).build(); HttpGet request =new HttpGet("http://www.domain.com"); response = client.execute(request); 复制代码
你将注意到具有DEBUG日志级别的重试逻辑:
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.domain.com/173.194.34.212:81 DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connect to www.domain.com/173.194.34.212:81 timed out. Connection will be retried using another IP address DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.domain.com/173.194.34.208:81 DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connect to www.domain.com/173.194.34.208:81 timed out. Connection will be retried using another IP address DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.domain.com/173.194.34.209:81 DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connect to www.domain.com/173.194.34.209:81 timed out. Connection will be retried using another IP address //... 复制代码