<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go:你了解微服務的超時傳遞嗎?

          共 2304字,需瀏覽 5分鐘

           ·

          2021-11-15 08:02

          為什么需要超時控制?

          很多連鎖故障的場景下的一個常見問題是服務器正在消耗大量資源處理那些早已經超過客戶端截止時間的請求,這樣的結果是,服務器消耗大量資源沒有做任何有價值的工作,回復已經超時的請求是沒有任何意義的。

          超時控制可以說是保證服務穩(wěn)定性的一道重要的防線,它的本質是快速失敗(fail fast),良好的超時控制策略可以盡快清空高延遲的請求,盡快釋放資源避免請求的堆積。

          服務間超時傳遞

          如果一個請求有多個階段,比如由一系列 RPC 調用組成,那么我們的服務應該在每個階段開始前檢查截止時間以避免做無用功,也就是要檢查是否還有足夠的剩余時間處理請求。

          一個常見的錯誤實現(xiàn)方式是在每個 RPC 服務設置一個固定的超時時間,我們應該在每個服務間傳遞超時時間,超時時間可以在服務調用的最上層設置,由初始請求觸發(fā)的整個 RPC 樹會設置同樣的絕對截止時間。例如,在服務請求的最上層設置超時時間為3s,服務A請求服務B,服務B執(zhí)行耗時為1s,服務B再請求服務C這時超時時間剩余2s,服務C執(zhí)行耗時為1s,這時服務C再請求服務D,服務D執(zhí)行耗時為500ms,以此類推,理想情況下在整個調用鏈里都采用相同的超時傳遞機制。

          如果不采用超時傳遞機制,那么就會出現(xiàn)如下情況:

          1. 服務A給服務B發(fā)送一個請求,設置的超時時間為3s
          2. 服務B處理請求耗時為2s,并且繼續(xù)請求服務C
          3. 如果使用了超時傳遞那么服務C的超時時間應該為1s,但這里沒有采用超時傳遞所以超時時間為在配置中寫死的3s
          4. 服務C繼續(xù)執(zhí)行耗時為2s,其實這時候最上層設置的超時時間已截止,如下的請求無意義
          5. 繼續(xù)請求服務D

          如果服務B采用了超時傳遞機制,那么在服務C就應該立刻放棄該請求,因為已經到了截止時間,客戶端可能已經報錯。我們在設置超時傳遞的時候一般會將傳遞出去的截止時間減少一點,比如100毫秒,以便將網絡傳輸時間和客戶端收到回復之后的處理時間考慮在內。

          進程內超時傳遞

          不光服務間需要超時傳遞進程內同樣需要進行超時傳遞,比如在一個進程內串行的調用了Mysql、Redis和服務B,設置總的請求時間為3s,請求Mysql耗時1s后再次請求Redis這時的超時時間為2s,Redis執(zhí)行耗時500ms再請求服務B這時候超時時間為1.5s,因為我們的每個中間件或者服務都會在配置文件中設置一個固定的超時時間,我們需要取剩余時間和設置時間中的最小值。

          context實現(xiàn)超時傳遞

          context原理非常簡單,但功能卻非常強大,go的標準庫也都已實現(xiàn)了對context的支持,各種開源的框架也實現(xiàn)了對context的支持,context已然成為了標準,超時傳遞也依賴context來實現(xiàn)。

          我們一般在服務的最上層通過設置初始context進行超時控制傳遞,比如設置超時時間為3s

          ctx,?cancel?:=?context.WithTimeout(context.Background(),?time.Second*3)
          defer?cancel()

          當進行context傳遞的時候,比如上圖中請求Redis,那么通過如下方式獲取剩余時間,然后對比Redis設置的超時時間取較小的時間

          dl,?ok?:=?ctx.Deadline()
          timeout?:=?time.Now().Add(time.Second?*?3)
          if?ok?:=?dl.Before(timeout);?ok?{
          ?timeout?=?dl
          }

          服務間超時傳遞主要是指 RPC 調用時候的超時傳遞,對于 gRPC 來說并不需要要我們做額外的處理,gRPC 本身就支持超時傳遞,原理和上面差不多,是通過 metadata 進行傳遞,最終會被轉化為 grpc-timeout 的值,如下代碼所示 grpc-go/internal/transport/handler_server.go:79

          if?v?:=?r.Header.Get("grpc-timeout");?v?!=?""?{
          ??to,?err?:=?decodeTimeout(v)
          ??if?err?!=?nil?{
          ???return?nil,?status.Errorf(codes.Internal,?"malformed?time-out:?%v",?err)
          ??}
          ??st.timeoutSet?=?true
          ??st.timeout?=?to
          }

          超時傳遞是保證服務穩(wěn)定性的一道重要防線,原理和實現(xiàn)都非常簡單,你們的框架中實現(xiàn)了超時傳遞了嗎?如果沒有的話就趕緊動起手來吧。

          go-zero 中的超時傳遞

          go-zero 中可以通過配置文件中的 Timeout 配置 api gatewayrpc 服務的超時,并且會在服務間自動傳遞。

          之前的 一文搞懂如何實現(xiàn) Go 超時控制 里面有講解超時控制如何使用。

          參考

          《SRE:Google運維解密》

          項目地址

          https://github.com/zeromicro/go-zero

          歡迎使用 go-zerostar/fork 支持我們!



          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學習資料禮包,包含學習建議:入門看什么,進階看什么。關注公眾號 「polarisxu」,回復?ebook?獲取;還可以回復「進群」,和數(shù)萬 Gopher 交流學習。

          瀏覽 45
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91久久婷婷国产麻豆精品电影.co | 猫先生大连劲爆身材语文 | 久久伊人国产一区 | 日韩做爱网站 | 国产操逼免费视频 |