最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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í)行結果如下

協(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ā)調用redis),而在業(yè)務執(zhí)行到并發(fā)調用redis之前,業(yè)務邏輯就已經(jīng)發(fā)生了超時。

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

前面已經(jīng)提到過,goroutine是無法強制殺死的,此時goroutine攜帶著已經(jīng)超時的context依舊在執(zhí)行著業(yè)務邏輯,在執(zhí)行到并發(fā)調用redis時,由于context已經(jīng)超時,調用無一例外的全部拋出超時錯誤(實際上并未真正發(fā)生調用redis,redis客戶端代碼在調用前判斷了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),如果判斷超時,則直接結束后續(xù)的業(yè)務流程)

日志告警清凈了!

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

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

最后

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

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

相關文章

  • Go實現(xiàn)一個配置包詳解

    Go實現(xiàn)一個配置包詳解

    在現(xiàn)代軟件開發(fā)中,配置文件是不可或缺的一部分。在編寫 Go 項目時,程序的靈活性和可擴展性都需要依賴于配置文件的加載。本文就來探究下在 Go 項目中如何更加方便的加載和管理配置,感興趣的朋友跟著小編一起來學習吧
    2023-04-04
  • Go?處理大數(shù)組使用?for?range?和?for?循環(huán)的區(qū)別

    Go?處理大數(shù)組使用?for?range?和?for?循環(huán)的區(qū)別

    這篇文章主要介紹了Go處理大數(shù)組使用for?range和for循環(huán)的區(qū)別,對于遍歷大數(shù)組而言,for循環(huán)能比for?range循環(huán)更高效與穩(wěn)定,這一點在數(shù)組元素為結構體類型更加明顯,下文具體分析感興趣得小伙伴可以參考一下
    2022-05-05
  • 一文帶你分析Go語言中的http請求過程

    一文帶你分析Go語言中的http請求過程

    在go中開發(fā)后端,最基礎的就是使用net/http包,本文將使用一個hello,world程序來進行debug,來探究在Go語言中http請求的過程,希望對大家有所幫助
    2026-02-02
  • 如何用Go語言操作MySQL示例詳解

    如何用Go語言操作MySQL示例詳解

    MySQL是業(yè)界常用的關系型數(shù)據(jù)庫,這篇文章主要介紹了如何用Go語言操作MySQL的相關資料,文中通過代碼介紹的非常詳細,對大家學習或者使用go語言操作mysql具有一定的參考借鑒價值,需要的朋友可以參考下
    2025-10-10
  • 利用go語言實現(xiàn)查找二叉樹中的最大寬度

    利用go語言實現(xiàn)查找二叉樹中的最大寬度

    這篇文章主要介紹了利用go語言實現(xiàn)查找二叉樹中的最大寬度,文章圍繞主題展開詳細介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-05-05
  • Golang并發(fā)之RWMutex的用法詳解

    Golang并發(fā)之RWMutex的用法詳解

    在?Go?語言中,RWMutex?是一種讀寫互斥鎖的實現(xiàn),它提供了一種簡單有效的方式來管理對共享資源的并發(fā)訪問。本文就來和大家詳細聊聊RWMutex的用法吧
    2023-04-04
  • golang一些常用的靜態(tài)檢查工具詳解

    golang一些常用的靜態(tài)檢查工具詳解

    這篇文章主要介紹了golang一些常用的靜態(tài)檢查工具,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • 解析golang中的并發(fā)安全和鎖問題

    解析golang中的并發(fā)安全和鎖問題

    本文我們來學習一下golang中的并發(fā)安全和鎖問題,文章通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-11-11
  • 詳解如何使用Bazel構建Golang程序

    詳解如何使用Bazel構建Golang程序

    這篇文章主要為大家介紹了如何使用Bazel構建Golang程序實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • golang簡易實現(xiàn)?k8s?的yaml上傳并應用示例方案

    golang簡易實現(xiàn)?k8s?的yaml上傳并應用示例方案

    這篇文章主要為大家介紹了golang簡易實現(xiàn)?k8s?的yaml上傳并應用示例方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07

最新評論

延寿县| 翁源县| 商水县| 卢湾区| 巴塘县| 金溪县| 兰西县| 宁南县| 无为县| 哈密市| 秦安县| 和顺县| 宁海县| 商城县| 扬州市| 新邵县| 湟源县| 苏尼特右旗| 新疆| 浑源县| 西乡县| 抚顺县| 门头沟区| 探索| 南陵县| 东丽区| 金乡县| 万年县| 乐东| 勃利县| 防城港市| 安平县| 台北市| 达州市| 上高县| 闻喜县| 乐业县| 临潭县| 吉木萨尔县| 龙井市| 醴陵市|