WCF 與 HTTP Keep-Alive

在診斷 WCF 連線異常關閉的過程中,曾經嘗試把 HTTP Keep-Alive 關閉。這和原先要診斷的問題可能無甚關聯,只是順手記一下。

什麼是 HTTP Keep-Alive?

當瀏覽器向伺服器要求多項資源時(例如 .html、.jpg、.js 等等),伺服器會一直保持目前的連線一段時間,以便將這些資源傳遞至用戶端瀏覽器,如此便可避免每傳遞一項資源就要建立一次連線的效能損耗。這就是 keep-alive 的作用。

從 HTTP 1.1 開始,用戶端無需特別指定,預設就是啟用 keep-alive。

可疑狀況

之所以會注意這東西,是因為手邊正在處理一個捉摸不定的問題:透過 WCF 呼叫別人家的 web services 時,偶爾會出現伺服器端突然關閉連線的狀況。應用程式捕捉到的 .NET 錯誤訊息是:

The underlying connection was closed: The connection was closed unexpectedly.

目前仍在實驗觀察階段,雖然覺得此問題與 keep-alive 的關係不大,但是多學點東西總是好的。我的疑問:
  • 就算用戶端指定要 keep-alive,伺服器端一定買單嗎?(keep-alive 的作用方式是 hop-by-hop
  • 就算伺服器買單,保持連線,但會不會剛好在用戶端發出的第 n 個請求時正好碰到伺服器認為時間到了或其他原因,片面將連線關閉?

都只是懷疑。目前也沒別的辦法,對方的伺服器遠在天邊,既無權限存取,也無從獲得相關資訊,例如環境組態、log 等等,只好看看自己還能做什麼了。(謎音:歡迎來到真實世界)

那就試試把 keep-alive 關閉吧。Why not?!

在 WCF 應用程式中關閉 Keep-Alive

WCF 的 HTTP 傳輸方式,預設也是啟用 Keep-Alive。如欲關閉,得改用 CustomBinding。

直接拿先前的筆記<WCF BasicHttpBinding 加密傳輸與身分驗證>裡面的範例稍微修改,變成這樣(主要變動是第 17~22 行):

void CallServicesByCode()
{
    string url = "https://target.service.company.com:123/qoo/QooServices";
    var endPoint = new EndpointAddress(url);

    var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
    binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;

    // 利用既有的 BasicHttpBinding 物件來建立 binding 元素集合,然後修改集合中的元素
    var bindingElements = binding.CreateBindingElements();

    // 修改 HTTP 傳輸繫結元素的 KeepAliveEnabled 屬性. 
    var transbe = bindingElements.Find<System.ServiceModel.Channels.HttpTransportBindingElement>();
    if (transbe != null)
    {        
        transbe.KeepAliveEnabled = false;
    }

    // 建立 CustomBinding 物件,使用剛才的 binding 元素集合.
    var customBinding = new System.ServiceModel.Channels.CustomBinding(bindingElements);

    var client = new Qoo.QooServicesClient(customBinding, endPoint); // 這裡改用 customBinding 物件.
    client.ClientCredentials.UserName.UserName = "Michael";
    client.ClientCredentials.UserName.Password = "guesswhat";

    // 測試呼叫遠端服務.
    var req = new Qoo.GetFooRequest();
    req.fooId = "1234";
    var resp = client.getFoo(req);
    txtResult = resp.fooName;
}

如果要用組態檔來設定 keep-alive 選項,MSDN 文件:WCF Load Balancing 裡面有範例可以參考。

延伸閱讀

沒有留言:

技術提供:Blogger.
回頂端⬆️