如何自定義長連線策略
此文已由作者趙慧莉授權網易雲社群釋出。
歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。
一、前言
最近在做內容分發網路(Content Delivery Network,簡稱 CDN)CDN的後端線上迴歸集監控時,常常出現連續執行多個用例時會報“org.apache.http.NoHttpResponseException”錯誤,而單個執行一個用例就不會報這種錯誤。經過分析為什麼介面測試時不會出現這種問題,而線上迴歸的時候就會觸發這種錯誤呢?原因是由於線上的迴歸集是為了驗證實際的情況要檢查CDN是否部署成功,所以線上一個用例的執行時間是30分鐘之上,而線下介面測試只需要毫秒級別。接下來我將介紹下為什麼會出現“org.apache.http.NoHttpResponseException”,以及如何解決這種問題。
二、出現“org.apache.http.NoHttpResponseException”的原因
HttpClient的實現類為CloseableHttpClient。建立CloseableHttpClient例項有兩種方式: (1)使用CloseableHttpClient的工廠類HttpClients的方法來建立例項。HttpClients提供了根據各種預設配置來建立CloseableHttpClient例項的快捷方法。最簡單的例項化方式是呼叫HttpClients.createDefault()。 (2)使用CloseableHttpClient的builder類HttpClientBuilder,先對一些屬性進行配置,再呼叫build方法來建立例項。上面的HttpClients.createDefault()實際上呼叫的也就是HttpClientBuilder.create().build()。 但是在測試的時候連續執行多個方法的時候就會報錯,直接執行其中的一個測試方法就不會有這種問題:
java.lang.AssertionError: org.apache.http.NoHttpResponseException: nos-yq-yanlian.netease.com:8080 failed to respond at org.testng.Assert.fail(Assert.java:89) at com.netease.pubncdn.test.testcase.OnlineDomainDeployTest.testCreateHttpDomain(OnlineDomainDeployTest.java:192) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
經過查詢資料發現:Http規範沒有規定一個持久連線應該保持存活多久。有些Http伺服器使用非標準的Keep-Alive頭訊息和客戶端進行互動,伺服器端會保持數秒時間內保持連線。HttpClient也會利用這個頭訊息。如果伺服器返回的響應中沒有包含Keep-Alive頭訊息,HttpClient會認為這個連線可以永遠保持。然而,很多伺服器都會在不通知客戶端的情況下,關閉一定時間內不活動的連線,來節省伺服器資源。在某些情況下預設的策略顯得太樂觀,我們可能需要自定義連線存活策略。
三、自定義長連線策略的方法
通過如下程式碼可以自定義連線存活策略:
CloseableHttpClient client = HttpClients.custom() .setKeepAliveStrategy(keepAliveStrategy) .build();
ConnectionKeepAliveStrategy keepAliveStrategy = new ConnectionKeepAliveStrategy() { public long getKeepAliveDuration(HttpResponse response, HttpContext context) { // Honor 'keep-alive' header HeaderElementIterator it = new BasicHeaderElementIterator( response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { try { return Long.parseLong(value) * 1000; } catch(NumberFormatException ignore) { } } } HttpHost target = (HttpHost) context.getAttribute( HttpClientContext.HTTP_TARGET_HOST); if ("www.baidu.com".equalsIgnoreCase(target.getHostName())) { // Keep alive for 5 seconds only return 5 * 1000; } else { // otherwise keep alive for 30 seconds return 30 * 1000; } } };
四、CDN中如何解決報錯問題
通過(三)中的方式可以解決“org.apache.http.NoHttpResponseException”報錯的問題,但是考慮到實際的應用場景不適合此種方法。因為一次請求到下次請求之間的間隔為30分鐘以上,不適合將連線時間改為30分鐘以上(因為很多伺服器都會在不通知客戶端的情況下,關閉一定時間內不活動的連線,來節省伺服器資源)。 修改測試程式碼為不在@BeforeClass時進行建立例項,而是在每個測試用例裡面進行建立例項來規避的解決這種問題。
五、總結
為了定位上述的報錯問題,需要了解使用HttpClient請求一個Http請求的步驟,以及如何自定義連線存活策略。在查到解決辦法後要考慮是否真正適合我們的應用場景,以及採用了這種方式是否會造成資源浪費,最終選擇適合的方式來規避的解決這個問題。
更多網易技術、產品、運營經驗分享請點選。