欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang服務中context超時處理的方法詳解

 更新時間:2023年05月22日 10:19:23   作者:皇家茶壺  
在Go語言中,Context是一個非常重要的概念,它存在于一個完整的業(yè)務生命周期內(nèi),Context類型是一個接口類型,在實際應用中,我們可以使用Context包來傳遞請求的元數(shù)據(jù),本文將給大家介紹Golang服務中context超時處理的方法和超時原因,需要的朋友可以參考下

前言

公司運行的服務代碼中,隨處可見各種各樣的日志信息,其中大多數(shù)是用來記錄各種異常的日志,一方面,當出現(xiàn)問題時,通過日志我們可以快速的定位引發(fā)問題的原因;另外我們可以通過日志平臺,對一些錯誤級別比較高的日志進行監(jiān)控,從而能夠快速響應系統(tǒng)可能會出現(xiàn)的問題。

起因:日志告警引發(fā)的思考

雖然日志告警很有用,但如果告警次數(shù)過于頻繁,反而會降低開發(fā)人員對于系統(tǒng)異常的敏感度,使得告警變得毫無意義。因此,我們需要對告警進行治理。最近,由于一次治理線上頻發(fā)的超時告警,使得筆者開始思考起context deadline exceed異常的問題。

什么是context

在Go語言中,Context是一個非常重要的概念,它存在于一個完整的業(yè)務生命周期內(nèi),Context類型是一個接口類型,它定義了四個方法:Deadline()、Done()、Err()和Value()。其中,Deadline()方法返回context的截止日期,Done()方法返回一個只讀的channel,當Context被取消或超時時,該channel會被關閉,Err()方法返回Context被取消的原因,Value()方法返回Context中與key相關聯(lián)的值。

context的作用

在實際應用中,我們可以使用Context包來傳遞請求的元數(shù)據(jù),例如請求ID、超時信息等等。此外,我們還可以使用context包來控制goroutine的生命周期(最常見的),例如在HTTP請求處理程序中,我們可以使用context包來取消正在處理的請求。

可以說,我們的服務里,隨處可見攜帶context參數(shù)的方法。

context超時之后

先來看一段例子

package main

import (
	"context"
	"fmt"
	"time"
)

func timeConsuming(ctx context.Context, costTime int) {

	ctx.Done()

	for i := 1; i <= costTime; i++ {
		// 模擬一些耗時操作
		time.Sleep(1 * time.Second)
		fmt.Printf("協(xié)程正在運行第%v次...\n", i)
	}
}

func main() {
	// 創(chuàng)建一個父級 context,設置超時時間為 5 秒鐘
	parentCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// 創(chuàng)建一個子級 context,用于控制協(xié)程
	childCtx, childCancel := context.WithCancel(parentCtx)
	defer childCancel()

	costTime := 5 // 模擬耗時 5 秒鐘

	// 啟動一個協(xié)程
	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				// 如果收到取消信號,退出協(xié)程
				fmt.Println("協(xié)程退出")
				return
			case <-time.After(15 * time.Second):
				fmt.Println("協(xié)程超時")
			default:
				timeConsuming(childCtx, costTime)
			}
		}
	}(childCtx)

	// 等待 3 秒鐘,然后取消子級 context
	time.Sleep(3 * time.Second)
	fmt.Println("取消協(xié)程")
	childCancel()

	// 繼續(xù)等待 3 秒鐘,模擬主協(xié)程的一些其他操作
	time.Sleep(3 * time.Second)
	fmt.Println("主協(xié)程退出")
}

上面代碼的執(zhí)行結(jié)果如下

協(xié)程正在運行第1次...
協(xié)程正在運行第2次...
取消協(xié)程
協(xié)程正在運行第3次...
協(xié)程正在運行第4次...
協(xié)程正在運行第5次...
協(xié)程退出
主協(xié)程退出

 雖然說Context可以用來管理goroutine,但是可以看到,Context超時之后,goroutine仍然在執(zhí)行完成之后才會退出,Context無法真正做到強制殺死goroutine

回到文章最開始提到的線上超時告警頻發(fā)的問題,經(jīng)過排查我們發(fā)現(xiàn),一波超時告警的出現(xiàn)實際上只是幾條請求引起的(都是同一個trace_id)。究其原因,是我們下游的服務在單次業(yè)務請求中,會與很多第三方接口發(fā)生交互(在本篇文章的case是并發(fā)調(diào)用redis),而在業(yè)務執(zhí)行到并發(fā)調(diào)用redis之前,業(yè)務邏輯就已經(jīng)發(fā)生了超時。

超時后,上游調(diào)用端不再繼續(xù)等待響應,直接返回了超時異常。

前面已經(jīng)提到過,goroutine是無法強制殺死的,此時goroutine攜帶著已經(jīng)超時的context依舊在執(zhí)行著業(yè)務邏輯,在執(zhí)行到并發(fā)調(diào)用redis時,由于context已經(jīng)超時,調(diào)用無一例外的全部拋出超時錯誤(實際上并未真正發(fā)生調(diào)用redis,redis客戶端代碼在調(diào)用前判斷了context的狀態(tài)),
從而導致個位數(shù)的超時請求卻引起了大量日志的超時告警。

...
//If Done is not yet closed, Err returns nil.
// If Done is closed, Err returns a non-nil error explaining why:
// Canceled if the context was canceled
// or DeadlineExceeded if the context's deadline passed.
// After Err returns a non-nil error, successive calls to Err return the same error.
if ctx.Err() != nil { // 這里拋出了context deadline exceeded 異常
	return nil, ctx.Err()
}
...

繼續(xù)執(zhí)行 or 中斷

知道了問題,其實處理起來就比較容易了,我們將context的狀態(tài)的判斷改寫到了合適的位置(在一些耗時的節(jié)點之間判斷了context的狀態(tài),如果判斷超時,則直接結(jié)束后續(xù)的業(yè)務流程)

日志告警清凈了!

但是,這樣的處理方式具有普適性嗎?可以思考一下,在某些超時的情況中,即便上游已經(jīng)返回了超時異常,我們?nèi)匀幌M掠文軌驅(qū)⑦@次業(yè)務完整的執(zhí)行完。

舉一個例子,下游在執(zhí)行完返回之前,會將本次執(zhí)行的結(jié)果進行緩存。而上游在調(diào)用下游之前,也會去取緩存,取到了就直接返回(假設上下游服務共用一套緩存集群)。假如某些請求耗時比較久,而且我們在判斷請求超時之后直接中斷下游任務的執(zhí)行,那么,緩存將永遠不會生成,上游后續(xù)的調(diào)用依舊會超時。這種情況下,即便是超時了,我們也希望下游任務能夠完整執(zhí)行,并生成緩存,后續(xù)上游就可以直接拿到業(yè)務結(jié)果返回,避免大量耗時的調(diào)用。

最后

本篇描述的本身是一個極為常見的問題及處理方案。但是在平時處理問題的過程中,如果勤加思考,仍然會有所收獲和提升。

以上就是Golang服務中context超時處理的方法詳解的詳細內(nèi)容,更多關于Golang context超時處理的資料請關注腳本之家其它相關文章!

相關文章

  • Go?語言?json解析框架與?gjson?詳解

    Go?語言?json解析框架與?gjson?詳解

    這篇文章主要介紹了Go語言json解析框架與gjson,JSON?解析是我們不可避免的常見問題,在Go語言中,我們可以借助gjson庫來方便的進行json屬性的提取與解析,需要的朋友可以參考一下
    2022-07-07
  • golang 結(jié)構(gòu)體初始化時賦值格式介紹

    golang 結(jié)構(gòu)體初始化時賦值格式介紹

    這篇文章主要介紹了golang 結(jié)構(gòu)體初始化時賦值格式介紹,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言文件操作的方法

    Go語言文件操作的方法

    這篇文章主要介紹了Go語言文件操作的方法,涉及文件的讀寫及關閉等操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • Go標準庫-ServeMux的使用與模式匹配深入探究

    Go標準庫-ServeMux的使用與模式匹配深入探究

    這篇文章主要為大家介紹了Go標準庫-ServeMux的使用與模式匹配深入探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01
  • go語言template用法實例

    go語言template用法實例

    這篇文章主要介紹了go語言template用法,實例分析了template的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • Golang Defer作用域及執(zhí)行順序使用案例

    Golang Defer作用域及執(zhí)行順序使用案例

    這篇文章主要為大家介紹了Golang Defer作用域及執(zhí)行順序使用案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • 如何go語言比較兩個對象是否深度相同

    如何go語言比較兩個對象是否深度相同

    這篇文章主要介紹了如何go語言比較兩個對象是否深度相同,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-05-05
  • golang?gorm的關系關聯(lián)實現(xiàn)示例

    golang?gorm的關系關聯(lián)實現(xiàn)示例

    這篇文章主要為大家介紹了golang?gorm的關系關聯(lián)實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2022-04-04
  • Go語言結(jié)合validator包實現(xiàn)表單驗證

    Go語言結(jié)合validator包實現(xiàn)表單驗證

    在現(xiàn)代?Web?開發(fā)中,表單驗證和錯誤處理是至關重要的環(huán)節(jié),本文將演示如何使用?Go?語言的?Gin?框架結(jié)合?validator?包,實現(xiàn)高級的表單驗證功能,需要的可以參考下
    2024-11-11
  • golang打包成帶圖標的exe可執(zhí)行文件

    golang打包成帶圖標的exe可執(zhí)行文件

    這篇文章主要給大家介紹了關于golang打包成帶圖標的exe可執(zhí)行文件的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2023-06-06

最新評論