1. 程式人生 > 程式設計 >GoLang之使用Context控制請求超時的實現

GoLang之使用Context控制請求超時的實現

起因

  之前接觸了一個需求:提供一個介面,這個介面有一個超時時間,如果超時了返回超時異常;這個介面中呼叫其他的介面,如果呼叫超時了,所有請求全部結束。
  在這個介面中,我使用了go協程去呼叫其他介面,所以不僅涉及到請求的超時控制,而且還涉及到父協程對子協程的控制問題。在翻閱了一些資料之後,瞭解到了Context的基本知識。

Context

  Context是golang.org.pkg下的一個包,型別是介面型別。主要功能有

父協程控制所有的子協程

  Context可以通過context.Background()

或者 context.TODO()建立一個空的context。兩個區別在於TODO

GoLang之使用Context控制請求超時的實現

context可以進行派生,創建出子context。context有四種不同的子context:
  (1)WithCancel:方法入參是一個context;返回值是一個帶有新Done的父context的副本,以及cancel函式。當呼叫cancel函式時,通道將被關閉。關閉規則:會先關閉內部的接收通道;通道關閉了接收該通道的操作會立即返回(即done返回的通道),並且context會向它所有的子值傳遞訊號,如果子context還有子context,那這個撤銷訊號就會一級一級傳遞下去。最後這個context會斷開其與父context的連線。

  (2)WithDeadlineWithTimeout(本次問題解決就使用的是這個):WithDeadline或者WithTimeout的功能極為相似。都是返回可以被撤銷的Context子值。它們不但可以被手動撤銷,還會依據在生成是給定的過期時間,自動地進行定時撤銷。
  WithDeadline是設定一個時間點,時間對上了就到期。WithTimeout是設定一段時間,比如幾秒,過個這段時間,就超時。其實底層的WithTimeout也是通過WithDeadline實現的。WithTimeout的呼叫就等於WithDeadline(parent,time.Now().Add(timeout))(其中parent是父級context)
  (3)WithValue:入參是父級parent,儲存的鍵和儲存的值。返回的是一個帶有資料的Context。這個Context是不能被撤銷的。撤銷的訊號在傳遞的時候會跳過這個Context。

協程間共享資料

  協程間共享資料主要使用的就是WithValue生成的子Context,這個Context存的值在其他的協程中也能讀取到。可以用做資料的共享。

超時取消協程

  主要用到的是WithDeadline生成的子Context以及Go中HttpClient請求中的context欄位(下文會有描述)

取消超時請求的模型

排程模型

GoLang之使用Context控制請求超時的實現
  

其中,對於超時的判斷,是根據Context中的Done管道判斷的。如果超時了,則Done管道可以拿到東西。

超時之後取消請求

  使用http.NewRequest方法獲取到的req,可以呼叫WithContext將定義好的WithTimeout型別的context放進去,之後呼叫&htto.Client{}.Do()方法即可。網上有一些部落格中讓手動呼叫transport中的CancelRequest方法,但是這個方法已經不被建議使用了。因為它不能取消Http/2的請求。

GoLang之使用Context控制請求超時的實現

 現在在程式碼中有一個私有化的roundTrip方法,會呼叫CancelRequest呼叫的cancelRequest方法。而這個roundTrip在transport中會在外面包一層RoundTrip方法,之後交給Client中的send方法進行呼叫。(具體可以進行原始碼的查閱)。所以現在通過Client的Do方法,可以自動完成請求的超時控制。

結論

  該排程模型親測之後,確實可以實現請求的超時控制。只要在最外層設定超時時間時30s,只要過了30s,所有協程中的請求都會結束,對應的協程也會相應的結束;加上Client.Do方法,將超時控制變的更加簡潔,後續會寫專門寫一篇關於http中Client的部落格,詳細講解一下Client實現超時控制的原理。

到此這篇關於GoLang之使用Context控制請求超時的實現的文章就介紹到這了,更多相關GoLang Context控制請求超時 內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!