日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

使用httpclient必須知道的參數(shù)設(shè)置及代碼寫法、存在的風(fēng)險(xiǎn)

 昵稱20874412 2015-05-05


結(jié)論:
如果使用httpclient 3.1并發(fā)量比較大的項(xiàng)目,最好升級(jí)到httpclient4.2.3上,保證并發(fā)量大時(shí)能抗住。httpclient 4.3.3,目前還有一些bug;還是用4.2.x穩(wěn)定版本吧。
 
以庫(kù)存項(xiàng)目為例:

httpclient一天并發(fā)量在1500w左右,峰值一秒7萬(wàn)。

 

在之前使用過(guò)程中,一直存在大量的

 

org.apache.http.conn.ConnectionPoolTimeoutExceptionTimeout waiting for connection from pool
atorg.apache.http.impl.conn.PoolingClientConnectionManager.leaseConnection(PoolingClientConnectionManager.java:232)
atorg.apache.http.impl.conn.PoolingClientConnectionManager$1.getConnection(PoolingClientConnectionManager.java:199)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:456)
另外通過(guò)jstack查看線程,會(huì)發(fā)現(xiàn):
"pool-21-thread-3" prio=10 tid=0x00007f6b7c002800 nid=0x40ff waiting on condition [0x00007f6b37020000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f97918b8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkUntil(LockSupport.java:239)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitUntil(AbstractQueuedSynchronizer.java:2072)
at org.apache.http.pool.PoolEntryFuture.await(PoolEntryFuture.java:129)
at org.apache.http.pool.AbstractConnPool.getPoolEntryBlocking(AbstractConnPool.java:281)
at org.apache.http.pool.AbstractConnPool.access$000(AbstractConnPool.java:62)
at org.apache.http.pool.AbstractConnPool$2.getPoolEntry(AbstractConnPool.java:176)
at org.apache.http.pool.AbstractConnPool$2.getPoolEntry(AbstractConnPool.java:172)
at org.apache.http.pool.PoolEntryFuture.get(PoolEntryFuture.java:100)
at org.apache.http.impl.conn.PoolingClientConnectionManager.leaseConnection(PoolingClientConnectionManager.java:212)
 
 
問(wèn)題:
因?yàn)槭褂昧诉B接池,但連接不夠用,造成大量的等待;而且這種等待都有滾雪球的效應(yīng)(和交易組最近使用的apache common dbcp存在的風(fēng)險(xiǎn)是類似的)。
 
 
解決方案
最終我們定了一些合理的參數(shù)值,目前來(lái)看還沒(méi)有遇到問(wèn)題。
 
 
思考
其實(shí)出問(wèn)題的原因是我們對(duì)一些參數(shù)不了解,隨意設(shè)置其值,不出現(xiàn)問(wèn)題則好,出現(xiàn)問(wèn)題很難排查到原因,因此我把使用httpclient必須設(shè)置的參數(shù)及代碼寫法及排查方法總結(jié)一下,供參考。
 
參數(shù)設(shè)置
1、httpclient 4.2.3
HttpParams params = new BasicHttpParams();
//設(shè)置連接超時(shí)時(shí)間
Integer CONNECTION_TIMEOUT = 2 * 1000; //設(shè)置請(qǐng)求超時(shí)2秒鐘 根據(jù)業(yè)務(wù)調(diào)整
Integer SO_TIMEOUT = 2 * 1000; //設(shè)置等待數(shù)據(jù)超時(shí)時(shí)間2秒鐘 根據(jù)業(yè)務(wù)調(diào)整
//定義了當(dāng)從ClientConnectionManager中檢索ManagedClientConnection實(shí)例時(shí)使用的毫秒級(jí)的超時(shí)時(shí)間
//這個(gè)參數(shù)期望得到一個(gè)java.lang.Long類型的值。如果這個(gè)參數(shù)沒(méi)有被設(shè)置,默認(rèn)等于CONNECTION_TIMEOUT,因此一定要設(shè)置
Long CONN_MANAGER_TIMEOUT = 500L; //該值就是連接不夠用的時(shí)候等待超時(shí)時(shí)間,一定要設(shè)置,而且不能太大 ()
 
params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, CONNECTION_TIMEOUT);
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, SO_TIMEOUT);
params.setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, CONN_MANAGER_TIMEOUT);
//在提交請(qǐng)求之前 測(cè)試連接是否可用
params.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, true);
 
PoolingClientConnectionManager conMgr = new PoolingClientConnectionManager();
conMgr.setMaxTotal(200); //設(shè)置整個(gè)連接池最大連接數(shù) 根據(jù)自己的場(chǎng)景決定
//是路由的默認(rèn)最大連接(該值默認(rèn)為2),限制數(shù)量實(shí)際使用DefaultMaxPerRoute并非MaxTotal。
//設(shè)置過(guò)小無(wú)法支持大并發(fā)(ConnectionPoolTimeoutException: Timeout waiting for connection from pool),路由是對(duì)maxTotal的細(xì)分。
conMgr.setDefaultMaxPerRoute(conMgr.getMaxTotal());//(目前只有一個(gè)路由,因此讓他等于最大值)
 
//另外設(shè)置http client的重試次數(shù),默認(rèn)是3次;當(dāng)前是禁用掉(如果項(xiàng)目量不到,這個(gè)默認(rèn)即可)
httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
此處解釋下MaxtTotal和DefaultMaxPerRoute的區(qū)別:
1、MaxtTotal是整個(gè)池子的大??;
2、DefaultMaxPerRoute是根據(jù)連接到的主機(jī)對(duì)MaxTotal的一個(gè)細(xì)分;比如:
MaxtTotal=400 DefaultMaxPerRoute=200
而我只連接到http://時(shí),到這個(gè)主機(jī)的并發(fā)最多只有200;而不是400;
而我連接到http:// 和 http://qq.com時(shí),到每個(gè)主機(jī)的并發(fā)最多只有200;即加起來(lái)是400(但不能超過(guò)400);所以起作用的設(shè)置是DefaultMaxPerRoute。
 
 
2、httpclient 3.1
HttpConnectionManagerParams params = new HttpConnectionManagerParams();
params.setConnectionTimeout(2000);
params.setSoTimeout(2000);
// 最大連接數(shù)
params.setMaxTotalConnections(500);
params.setDefaultMaxConnectionsPerHost(500);
params.setStaleCheckingEnabled(true);
connectionManager.setParams(params);
 
HttpClientParams httpClientParams = new HttpClientParams();
// 設(shè)置httpClient的連接超時(shí),對(duì)連接管理器設(shè)置的連接超時(shí)是無(wú)用的
httpClientParams.setConnectionManagerTimeout(5000); //等價(jià)于4.2.3中的CONN_MANAGER_TIMEOUT
httpClient = new HttpClient(connectionManager);
httpClient.setParams(httpClientParams);
 
//另外設(shè)置http client的重試次數(shù),默認(rèn)是3次;當(dāng)前是禁用掉(如果項(xiàng)目量不到,這個(gè)默認(rèn)即可)
httpClientParams.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(0, false));
 
參數(shù)類似 就不多解釋了;
 
代碼寫法
1、httpclient 4.2.3
HttpResponse response = null;
HttpEntity entity = null;
try {
  HttpGet get = new HttpGet();
  String url = "http://hc./";
  get.setURI(new URI(url));
  response = getHttpClient().execute(get);
/  /處理響應(yīng)
} catch (Exception e) {
  //處理異常
} finally {
  if(response != null) { 
    EntityUtils.consume(response.getEntity()); //會(huì)自動(dòng)釋放連接
  }
  //如下方法也是可以的,但是存在一些風(fēng)險(xiǎn);不要用
  //InputStream is = response.getEntity().getContent();
  //is.close();
}
 
2、httpclient 3.1
PostMethod postMethod = new PostMethod(yxUrl);
try { 
  httpClient.executeMethod(postMethod);
} catch (Exception e) {
  //處理異常
} finally {
  if(postMethod != null) { //不要忘記釋放,盡量通過(guò)該方法實(shí)現(xiàn),
    postMethod.releaseConnection();
    //存在風(fēng)險(xiǎn),不要用
    //postMethod.setParameter("Connection", "close");
    //InputStream is = postMethod.getResponseBodyAsStream();
    //is.clsoe();也會(huì)關(guān)閉并釋放連接的
  }
}
 
存在的風(fēng)險(xiǎn)
1、httpclient 4.2.3 在釋放連接時(shí)
if (managedConn.isOpen() && !managedConn.isMarkedReusable()) { //如果連接打開的且不可重用(not keepalive) close socket
  try {
    managedConn.shutdown();
  } catch (IOException iox) {
    if (this.log.isDebugEnabled()) {
      this.log.debug("I/O exception shutting down released connection", iox);
    }
  }
}
// Only reusable connections can be kept alive
if (managedConn.isMarkedReusable()) {
  entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
  if (this.log.isDebugEnabled()) {
    String s;
    if (keepalive > 0) {
      s = "for " + keepalive + " " + tunit;
    } else { 
      s = "indefinitely";
    }
    this.log.debug("Connection " + format(entry) + " can be kept alive " + s);
  }
}
無(wú)風(fēng)險(xiǎn)
 
2、httpclient 3.1
1、如果走h(yuǎn)ttp1.1協(xié)議:如果proxy-connection/connection請(qǐng)求頭設(shè)置為close;那么會(huì)關(guān)閉socket; 或者這兩個(gè)頭不等于close 也會(huì)自動(dòng)關(guān);
2、如果是keep-alive ,不會(huì)關(guān)閉;
3、如果協(xié)議小于等于http1.0協(xié)議沒(méi)有問(wèn)題;調(diào)用releaseConnection時(shí)會(huì)close socket;
4、其他情況不會(huì)close;
 
也就是說(shuō)如果走h(yuǎn)ttp1.1且沒(méi)有設(shè)置相關(guān)參數(shù);那么socket其實(shí)是沒(méi)有關(guān)閉的;可能造成很多TIME_WAIT;因此如果是走短連接建議設(shè)置postMethod.setParameter("Connection", "close")。
 
其他注意事項(xiàng):
1、使用keep-alive一定要設(shè)置Content-Length頭(否則也不是長(zhǎng)連接)。
 
2、在使用httpclient3.1時(shí)(4.2.3沒(méi)問(wèn)題);盡量不要調(diào)用 byte[] getResponseBody() :因?yàn)槿绻鸆ontent-Length沒(méi)設(shè)置或者傳輸?shù)臄?shù)據(jù)大于1M,會(huì)有大量如下日志
LOG.warn("Going to buffer response body of large or unknown size. "
+"Using getResponseBodyAsStream instead is recommended.");
 
如果大于1M可以設(shè)置該參數(shù);但是-1的話就沒(méi)辦法了,就不要調(diào)用 byte[] getResponseBody()
httpClientParams.setLongParameter(HttpMethodParams.BUFFER_WARN_TRIGGER_LIMIT, 2L * 1024 * 1024);
 
 
3、鎖
httpclient 3.1 使用synchronized+wait+notifyAll,存在兩個(gè)問(wèn)題,量大synchronized慢和notifyAll可能造成線程饑餓;httpclient 4.2.3 使用 ReentrantLock(默認(rèn)非公平) + Condition(每個(gè)線程一個(gè))。
 
這里有個(gè)測(cè)試:http://java./articles/synchronized-vs-lock ,在我本機(jī)(jdk1.6.0_43 )測(cè)試結(jié)果明細(xì)鎖的優(yōu)勢(shì)比較大
1x synchronized {} with 32 threads took 2.621 seconds
1x Lock.lock()/unlock() with 32 threads took 1.951 seconds
1x AtomicInteger with 32 threads took 4.113 seconds
1x synchronized {} with 64 threads took 2.621 seconds
1x Lock.lock()/unlock() with 64 threads took 1.983 seconds
 
這也是為什么在庫(kù)存項(xiàng)目中使用httpclient 3.1 依然有大量的wait,而httpclient4.2.3 一個(gè)沒(méi)有的問(wèn)題所在。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多