Go 中如何準確地判斷和識別各種網絡錯誤
Go語言集成了簡單易用的網路庫,今天的推送是一線架構師分享的Go網絡庫使用入門指南。
Go 自帶的網絡標準庫可能讓很多第一次使用它的人感慨,這個庫讓網絡編程的門檻低到了令人發指的地步。然而,封裝層次與開發人員的可控性往往是矛盾的。Go 的網絡庫封裝程度算是一個不錯的折衷,絕大部分時候,我們只需要調用 Dial, Read, Write Close 幾個基本操作就可以了。
但是,網絡是復雜的。我們有時候需要細致的處理網絡中的各種錯誤,根據不同的錯誤進行不同的處理。比如我們遇到一個網絡錯誤時,需要區分這個錯誤是因為無法解析 host ip, 還是 TCP 無法建立連接,亦或是讀寫超時。一開始的時候,我們的寫法可能是這樣的:
這種根據錯誤信息進行字符串匹配進行判斷的方法有非常明顯的局限性:該錯誤信息依賴于操作系統,不同的操作系統對于同一錯誤返回的字符串信息可能是不同的。因此,這種判斷網絡錯誤類型的方法是不可靠的。那么有沒有一種準確而可靠的判斷各種網絡錯誤的方式呢?答案是肯定的。
我們知道在 Go 中,error 是一個內建的 interface 類型:
要準確判斷不同的錯誤類型,我們只需要類型斷言出其錯誤類型即可。
在 Go 的網絡標準庫中,錯誤類型被統一封裝為 net.Error 的 interface 類型:
而 net.Error 類型的具體 concrete 類型又被封裝為 net.OpError 類型:
其中,net.OpError.Err 可能是以下幾種類型:
-
net.DNSError -
net.InvalidAddrError -
net.UnknownNetworkError -
net.AddrError -
net.DNSConfigError -
*os.SyscallError
*os.SyscallError 錯誤比較特殊,與具體操作系統調用有關:
對于我們關心的網絡錯誤,SyscallError.Err 一般為 sys.Errno 類型,與網絡錯誤相關的常用值有:
-
syscall.ECONNREFUSED -
syscall.ETIMEDOUT
看到這里,你可能忍不住要吐槽 Go 這種錯誤嵌套處理了,事實上,官方也意識到了這種錯誤處理的問題,在 Go 2中,可能會出現新的錯誤和異常處理方式,可以參見 GopherChina 2018 keynote 點評: RETHINKING ERRORS FOR GO 2 (https://liudanking.com/arch/gopherchina-2018-keynote-%E7%82%B9%E8%AF%84/).
當前階段,我們依然要直面這種錯誤處理方式。為了方便大家理解 Go 網絡標準庫中處理錯誤的方式,我們把上面的錯誤嵌套整理了一張關系圖:
明白了網絡標準庫中處理錯誤的邏輯,判斷和識別各種類型的網絡錯誤就非常簡單了:對網絡錯誤進行類型斷言。以我們團隊主要關心的 DNS 解析錯誤、TCP 無法建立連接、讀寫超時為例,判斷邏輯可以是這樣:
這種錯誤判定方式除了能解決最開始提到的可靠性和準確性問題,也具有良好的普適性。即基于 net 的其他標準庫,如 net/http 也支持這種錯誤判斷方式。
轉自:
liudanking.com/network/go-%e4%b8%ad%e5%a6%82%e4%bd%95%e5%87%86%e7%a1%ae%e5%9c%b0%e5%88%a4%e6%96%ad%e5%92%8c%e8%af%86%e5%88%ab%e5%90%84%e7%a7%8d%e7%bd%91%e7%bb%9c%e9%94%99%e8%af%af/