golang服務健壯性---(異常處理)

來源:互聯網
上載者:User

在程式開發時我們都會認為外部提供的介面或者資料都是不可信的。比如函數總是要檢查入參的正確性,在做單元測試的時候要把外部提供的介面給屏蔽掉等。之所以都會這麼做,主要還是很難保證自己還是其他人可以提供一個沒有任何缺陷的介面。既然介面是人寫的,那麼多少會有些考慮不到的地方,這時候介面在被調用的時候就有可能發生錯誤或者異常。這裡討論golang中的異常處理機制,其實就是panic和recover這兩個介面的運用,類似於C++中的try和catch。
1、golang中的panic
panic,中文解釋為恐慌。舉個例子,單代碼中出現這樣的語句的時候,相信所有開發人員在產品上線的時候都會恐慌:

    var MakecoreData *int = nil    *MakecoreData = 10000

如果不做任何處理的時候,這個golang出現如果跑到這裡的時候就會出現這樣的結果。

panic: runtime error: invalid memory address or nil pointer dereference[signal 0xc0000005 code=0x1 addr=0x0 pc=0x5d0676]

然後重新就退出了,這個結果其實跟C/C++中的core是一樣的。
當然這個例子可能不會有人會犯這種低級錯誤,但是如果這兩個語句中間加上一段複製的邏輯,且需要一定的條件觸發的話,那可能就很難發現了。
再來看看golang中panic的機制,當程式發生異常的時候,golang語言層級的會拋出一個panic,以上面例子來說,當代碼運行到*MakecoreData = 10000的時候,會拋出一個panic,函數就結束了,如果沒有做任何處理,那麼程式中對應的g就異常,程式的生命週期就結束了。同樣的,如果在代碼中直接調用panic也是一致的結果。例如直接調用:panic(“have a panic”)的話,運行結果為:
panic(“have a panic”),然後顯示對應的堆棧資訊。

2,golang中的recover
在golang當異常發生的時候,都會產生一個panic。而一個panic總會有一個recover與之對應。當沒有任何處理的時候,預設的recover行為的行為是將進程退出。recover會返回一個error類型的傳回值,記錄異常的情況。由於當異常產生的時候,產生異常的函數將不再執行,會立即退出。那recover就只能在defer中調用,而且是要在產生異常前的defer語句中調用。因為在golang中,只有執行到defer語句的時候才會將對應的函數插入到defer隊列中,如果defer語句在異常產生後才調用,該defer對應要執行的函數由於沒有插入到defer隊列而沒有被調用到。
下面將以幾個例子來說明:
1)recover不在defer中:

import (    "fmt"    "net/http"    "runtime")func main() {    if err := recover(); err != nil {        fmt.Println(err)    }    var MakecoreData *int = nil    *MakecoreData = 10000    if err := recover(); err != nil {        fmt.Println(err)    }    fmt.Println("hello world")}

從代碼來看,產生異常的代碼前後都調用了recover,但是從結果來看,運行結果跟前後不加recover是一致的。

2)在異常代碼後增加一個defer語句,語句中有recover,例子如下:

import (    "fmt"    "net/http"    "runtime")func main() {    var MakecoreData *int = nil    *MakecoreData = 10000    defer func() {        if err := recover(); err != nil {            fmt.Println(err)        }    }()    fmt.Println("hello world")}

從結果來看,還是跟沒有加recover是一致的。

3、在發生異常前增加一個defer語句,語句中有recover。例子如下:

import (    "fmt"    "net/http"    "runtime")func main() {    defer func() {        if err := recover(); err != nil {            fmt.Println(err)        }    }()    var MakecoreData *int = nil    *MakecoreData = 10000    fmt.Println("hello world")}

這段代碼中recover才會真正的被執行。這就跟前面分析的一種。當遇到異常的時候,發生異常的代碼後面的函數體將不會被執行。函數退出,這時候會執行defer隊列中註冊的函數,如果在這裡有recover操作。那麼異常就會被捕獲到。然後函數退出,不會導致程式結束。

3、使用golang異常機制,增強代碼的健壯性。
從golang的異常機制可以看出,異常處理recover最好是在介面的入口處就將其插入到對應defer隊列中。這樣在介面調用過程中,即使發生異常,程式依然可以提供服務。以web服務為例子來看看如何使用golang的異常機制增強代碼的健壯性:
1、無任何異常處理機制的web服務如下:

import (    "fmt"    "log"    "net/http")func HelloServer(w http.ResponseWriter, req *http.Request) {    var MakecoreData *int = nil    *MakecoreData = 10000    fmt.Fprintf(w, "hello world")}func main() {    http.HandleFunc("/hello", HelloServer)    err := http.ListenAndServe(":12345", nil)    if err != nil {        log.Fatal("ListenAndServe: ", err)    }}

這個代碼很明顯,在執行業務代碼的時候會發生異常。異常產生的原因跟上面章節描述的一致。但是如果服務有多個頁面,不僅僅只有/hello的時,當用戶端調用localhost:12345/hello以後,會返回一個404返回碼。但是服務並不會掛掉。原因在於golang的http包中在接受一個請求的時候會有先在入口處調用recover來保證golang開發工程師在寫業務代碼的時候,如果發生了異常,不至於使整個服務都崩潰掉。

4、利用golang其他功能配合異常處理機制增強服務的健壯性。
利用golang的異常機制,可以是服務在有缺陷的情況下依然可以提供部分暫時沒有缺陷的服務。但是這樣是遠遠不夠的,因為發現缺陷後他們需要修複,修改後還要重新部署。
對於大部分服務來說,服務需要提供24*7的不間斷服務。那麼在解決方案中,需要做的更多了。比如:
1、定位異常點。
定位方法可以通過golang中runtime包擷取堆棧資訊及入參並儲存,可以知道程式在哪裡發生異常,這樣可以方便後續的維護。
2、採用主備服務
當確定某個服務存在缺陷的時候,採用主備,不接業務的伺服器升級可以減少由於更新服務帶來的業務中斷。
3、服務業務模組採用熱插拔外掛程式式
業務模組是服務中跑的最多的代碼。一般如果存在異常,那麼至少有90%以上出現在業務模組代碼上。如果採用外掛程式的形式,看在服務不停止的情況下更新模組代碼。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.