1. 程式人生 > >如何自定義長連線策略

如何自定義長連線策略

此文已由作者趙慧莉授權網易雲社群釋出。

歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。

一、前言

最近在做內容分發網路(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請求的步驟,以及如何自定義連線存活策略。在查到解決辦法後要考慮是否真正適合我們的應用場景,以及採用了這種方式是否會造成資源浪費,最終選擇適合的方式來規避的解決這個問題。

更多網易技術、產品、運營經驗分享請點選