不要信任預設超時

Brewin發表於2020-09-25

原文地址:Don’t trust default timeouts

現代應用程式不會崩潰但是它們會掛起。其中一個主要原因是假設網路可靠但是其實並不是。

當您在沒有設定超時的情況下進行網路呼叫時,您正在告訴您的程式碼,您百分之百確信該呼叫將成功。你真的願意打賭嗎?

如果您正處在一個永遠不會返回的同步網路呼叫中,那麼您的執行緒將永遠佔用。哎喲,不返回的非同步網路呼叫也不是免費的哦。當然,您沒有佔用執行緒,但您正在洩漏套接字。任何值得使用的HTTP客戶端庫都使用套接字池來避免重新建立連線,然而這些池子的容量是有限的。與其他資源洩漏一樣,沒有套接字只是時間問題。當這種情況發生時,您的應用程式將在等待連線釋放時卡住。

如果網路不可靠,為什麼我們要繼續建立以無限為預設超時的api?有些API一開始甚至沒有設定超時的方法!一個好的API應該容易用正確的方式使用,並且很難被錯誤的方式使用。當預設超時為無窮大時,客戶機很容易自食其果。

如果你記得這篇文章中的一件事,那就這樣做:永遠不要使用“無限”作為預設超時。

讓我們看一些具體的例子。

Javascript的XMLHttpRequest是從伺服器非同步檢索資料的web API。它的預設超時為零,這意味著沒有超時!

var xhr = new XMLHttpRequest();
xhr.open('GET', '/api', true);

// No timeout by default!
xhr.timeout = 10000; 

xhr.onload = function () {
 // Request finished
};

xhr.ontimeout = function (e) {
 // Request timed out
};

xhr.send(null);

客戶端超時與伺服器端超時一樣重要。瀏覽器存在為特定主機開啟的最大套接字數。如果您發出永不返回的網路請求,則會耗盡套接字池。當池耗盡時,您將無法再連線到主機。

fetch Web API 是XMLHttpRequest API的現代替代品,它使用承諾。最初引入API時,根本沒有辦法設定超時!不過,瀏覽器最近增加了對Abort API的實驗性支援,以支援超時。

const controller = new AbortController();

const signal = controller.signal;

const fetchPromise = fetch(url, {signal});  

// No timeout by default!
setTimeout(() => controller.abort(), 10000); 

fetchPromise.then(response => {
 // Request finished
})

在Python領域,事情並沒有那麼樂觀。請求庫使用預設超時infinity

# No timeout by default!
response = requests.get('https://github.com/', timeout=10)

那Go呢?預設情況下,Go的HTTP包也不使用超時

var client = &http.Client{
  // No timeout by default!
  Timeout: time.Second * 10, 
}

response, _ := client .Get(url)

適用於Java和.NET的現代HTTP客戶端做得更好,通常帶有預設超時。例如,.Net Core的HttpClient的預設超時為100秒。雖然時間比較寬鬆,但比根本沒有超時要好得多。他們會有預設超時並不奇怪,因為這些語言用於構建需要對網路故障進行穩定處理的大規模分散式系統。沒有超時的網路請求是分散式系統的最大沉默殺手

記住這一點

根據經驗,網路呼叫時請務必設定超時時間。而且,如果您構建庫,請始終設定合理的預設超時,並使它們可為您的客戶端配置。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章