HttpClient4中采用
ThreadSafeClientConnManager来保证线程的安全,优于2.0的
MultiThreadedHttpConnectionManager类。另外Apache官方强烈推荐只使用一个HttpClient的实例,所以我
们可以将以下demo方法写成单例模式。
demo将使用StringEntity来完成不指定参数名发送Post,已经采用ThreadSafeClientConnManager来保证线程的安全
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 |
public String sendToHttpClientGprs(String url,String content) { BufferedReader reader = null ; StringBuffer stringBuffer = null ; HttpParams httpParams = new
BasicHttpParams(); // 设置超时 ConnManagerParams.setTimeout(httpParams, 5 * 1000 ); HttpConnectionParams.setConnectionTimeout(httpParams, 5 * 1000 ); HttpConnectionParams.setSoTimeout(httpParams, 5 * 1000 ); // 多线程最大连接数 ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new
ConnPerRouteBean( 5 )); // 多线程总连接数 ConnManagerParams.setMaxTotalConnections(httpParams, 10 ); // 设置异常处理机制 HttpProtocolParams.setUseExpectContinue(httpParams, true ); // 设置是否检查旧连接 HttpConnectionParams.setStaleCheckingEnabled(httpParams, false ); // 设置版本 HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1); // 设置编码 HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8); // 设置重定向 HttpClientParams.setRedirecting(httpParams, false ); // 设置userAgent String userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2) Gecko/20100115 Firefox/3.6" ; HttpProtocolParams.setUserAgent(httpParams, userAgent); //设置是否延迟发送 HttpConnectionParams.setTcpNoDelay(httpParams, true ); //设置缓存大小 HttpConnectionParams.setSocketBufferSize(httpParams, DEFAULT_SOCKET_BUFFER_SIZE); // 支持http与https SchemeRegistry schemeRegistry = new
SchemeRegistry(); schemeRegistry.register( new
Scheme( "http" , PlainSocketFactory.getSocketFactory(), 80 )); schemeRegistry.register( new
Scheme( "https" , SSLSocketFactory.getSocketFactory(), 443 )); //ThreadSafeClientConnManager线程安全管理类 ClientConnectionManager manager = new
ThreadSafeClientConnManager(httpParams, schemeRegistry); HttpClient client = new
DefaultHttpClient(manager, httpParams); //post请求 HttpPost httppost = new
HttpPost(path); httppost.setHeader( "connection" , "Keep-Alive" ); httppost.setHeader( "Content-Type" , "text/plain" ); try { //直接传入发送内容,不指定参数名 StringEntity myEntity = new
StringEntity(content, "UTF-8" ); httppost.setEntity(myEntity); // 发送请求 HttpResponse response = client.execute(httppost); Log.i( "ResponseCode" , response.getStatusLine().getStatusCode() + "" ); // 请求成功 if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { reader = new
BufferedReader( new
InputStreamReader(response.getEntity().getContent())); stringBuffer = new
StringBuffer(); String line = "" ; while
((line = reader.readLine()) != null ) { stringBuffer.append(line); } } } catch
(Exception e) { e.printStackTrace(); } finally
{ try { // 关闭流 if ( null != reader) { reader.close(); reader = null ; } } catch
(IOException e) { e.printStackTrace(); } } if ( null == stringBuffer) { return
"-1" ; } else
{ return
stringBuffer.toString(); } } |
先看看以前的代码,每次请求都会创建一个HttpClient,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 |
HttpClient client = getHttpClient( 5000 , 5000 ); GetMethod getMethod = new
GetMethod(url); getMethod.setQueryString(parameter); byte [] responseBody = null ; try { int
statusCode = client.executeMethod(getMethod); if
(statusCode != HttpStatus.SC_OK) { logger.error( "error !"
+ url + ",statusCode "
+ statusCode); } responseBody = getMethod.getResponseBody(); } catch
(IOException e) { logger.error( "error!"
+ url, e); } finally
{ getMethod.releaseConnection(); } |
这是最简单的代码,很多例子就是这样开始的,如果少量请求还行,请求很频繁的话,这样的代码不是很好。(注意这里连接用完后必须释放连接)。
首先,HttpClient可以共用,减少创建HttpClient的开销。当然,如果你的应用调用HttpClient并不怎么频繁的话那就没必要共用了,毕竟在内存中维护一个空闲的httpClient对象是不保险的。
其次,Connection可以重用,减少建立连接的开销。
要完成以上两点,可以用多线程下的MultiThreadedHttpConnectionManager管理HttpConnection和
HttpClient。MultiThreadedHttpConnectionManager管理的HttpClient是线程安全的,可以做成单例
的。但是值得注意的是每个线程应该有自己的HttpMethod和HttpState、HttpConfiguration,以区分每次请求的host和
HttpSession 。如下所示:
private static MultiThreadedHttpConnectionManager httpConnectionManager = new MultiThreadedHttpConnectionManager(); private HttpClient client = new HttpClient(httpConnectionManager);
定义好后,在静态块中初始化相关参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 |
static { //每主机最大连接数和总共最大连接数,通过hosfConfiguration设置host来区分每个主机 client.getHttpConnectionManager().getParams().setDefaultMaxConnectionsPerHost( 8 ); client.getHttpConnectionManager().getParams().setMaxTotalConnections( 48 ); client.getHttpConnectionManager().getParams().setConnectionTimeout( 5000 ); client.getHttpConnectionManager().getParams().setSoTimeout( 5000 ); client.getHttpConnectionManager().getParams().setTcpNoDelay( true ); client.getHttpConnectionManager().getParams().setLinger( 1000 ); //失败的情况下会进行3次尝试,成功之后不会再尝试 client.getHttpConnectionManager().getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new
DefaultHttpMethodRetryHandler()); } |
HttpConfiguration中有一些参数对提高性能有一些帮助,主要说明如下:
DefaultMaxConnectionsPerHost参数定义每台主机允许的最大连接数,默认为2。这个参数只能用于一些特定的httpConnectionManager,比如MultiThreadedHttpConnectionManager。
MaxTotalConnections参数表示httpConnectionManager管理的最大连接数,默认为20。同上个参数,这个参数也只是在某些特定的httpConnectionManager中有用。
setTcpNoDelay(true)设置是否启用Nagle算法,设置true后禁用Nagle算法,默认为false(即默认启用Nagle算
法)。Nagle算法试图通过减少分片的数量来节省带宽。当应用程序希望降低网络延迟并提高性能时,它们可以关闭Nagle算法,这样数据将会更早地发
送,但是增加了网络消耗。
setLinger(1000)设置socket延迟关闭时间,值为0表示这个选项是关闭的,值为-1表示使用JRE的默认设置。
setStaleCheckingEnabled(true)参数设置是否启用旧连接检查,默认是开启的。关闭这个旧连接检查可以提高一点点性能,但是增
加了I/O错误的风险(当服务端关闭连接时)。开启这个选项则在每次使用老的连接之前都会检查连接是否可用,这个耗时大概在15-30ms之间[3]。
然后,在每个线程代码中,创建自己的HttpMethod。
还有一个很重要的优化点是采用请求/响应实体流,尤其是在请求频繁数据量大的情况下,很大的实体不会被缓存在内存中而直接发送或接收,采用流能有效的提高
性能。虽然这些实体可以是字符串或者字节数组,但是它们容易导致内存泄露,你得小心的使用他们,因为它们是整个实体缓存在内存中的。
对于响应流,采用method的getResponseBodyAsStream()来代替getResponseBody()
和getResponseBodyAsString().如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
HttpClient httpclient = new
HttpClient(); try
{ httpclient.executeMethod(httpget); Reader reader = new
InputStreamReader(httpget.getResponseBodyAsStream(), httpget.getResponseCharSet()); // consume the response entity } finally
{ httpget.releaseConnection(); } |
而对于请求流,可以通过实现RequestEntity自定义自己的各种流,httpclient包含了常见的几种实现,如 FileRequestEntity、ByteArrayRequestEntity、StringRequestEntity、 MultipartRequestEntity等。使用示例如下:
1
2
3
4
5 |
File myfile = new
File( "myfile.txt" ); PostMethod httppost = new
PostMethod( "/stuff" ); httppost.setRequestEntity( new
FileRequestEntity(myfile)); |
如果客户端和服务器的通讯不需要保持会话状态的话,可以通过禁用Cookie来提高一点点性能,比如蜘蛛爬虫之类应用。如下:
1
2
3
4
5 |
HttpMethod method = new
GetMethod(); method.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES); method.setRequestHeader( "Cookie" , "special-cookie=value" ); |
HttpClient有关方法及相关优化整理,布布扣,bubuko.com
原文:http://www.cnblogs.com/weixiao870428/p/3766124.html