Golang中context包使用場景和示例詳解
控制子協(xié)程退出
context包提供了一種機制,可以在多個goroutine之間進行通信和控制。使用Context包能夠有效地控制程序的并發(fā)性,提高程序的健壯性和性能。
Golang是沒有辦法讓其他goroutine退出的,goroutine只能自己退出。之所以說context包可以控制子協(xié)程退出意思是子協(xié)程可以接收到主協(xié)程發(fā)出的退出信號,然后自己退出??慈缦率纠a:
package main
import (
"context"
"errors"
"sync"
)
func request(ctx context.Context, url string) error {
result := make(chan int)
err := make(chan error)
go func() {
// 假如isSuccess是請求返回的結(jié)果,成功則通過result傳遞成功信息,錯誤通過error傳遞錯誤信息
isSuccess := true
if isSuccess {
result <- 1
} else {
err <- errors.New("some error happen")
}
}()
select {
case <-ctx.Done():
// 其他請求失敗
return ctx.Err()
case e := <-err:
// 本次請求失敗,返回錯誤信息
return e
case <-result:
// 本此請求成功,不返回錯誤信息
return nil
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
// 調(diào)用接口a
err := request(ctx, "https://xxx.com/a")
if err != nil {
return
}
wg := sync.WaitGroup{}
// 調(diào)用接口b
wg.Add(1)
go func() {
defer wg.Done()
err := request(ctx, "https://xxx.com/b")
if err != nil {
cancel()
}
}()
// 調(diào)用接口c
wg.Add(1)
go func() {
defer wg.Done()
err := request(ctx, "https://xxx.com/c")
if err != nil {
cancel()
}
}()
wg.Wait()
}首先調(diào)用context.WithCancel方法構(gòu)造了一個Context和返回了一個cancel函數(shù),其他goroutine調(diào)用的方法都傳入了這個Context作為第一個參數(shù),當(dāng)主goroutine想要告訴所有g(shù)oroutine需要退出的時候,通過調(diào)用cancel函數(shù)把退出的信息告訴所有的goroutine。所有g(shù)oroutine通過監(jiān)聽ctx.Done返回的channel得到退出信號然后退出。
超時控制
例如查詢數(shù)據(jù)庫、調(diào)用RPC服務(wù)、調(diào)用HTTP接口等場景,這些操作都是阻塞的,如果一直不返回數(shù)據(jù)的話,會影響產(chǎn)品的用戶體驗。針對這些情況的解決方式通常是設(shè)置一個超時間,超過后自動取消操作。
使用context包中的WithDeadline和WithTimeout方法可以實現(xiàn)這個解決方法。先看如下例子:
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // 輸出 "context deadline exceeded"
}
}因為設(shè)置的超時時間是50毫秒,所以select會進入第二個case,會輸出“context deadline exceeded”。
Golang的net/http包發(fā)起http請求的時候是實現(xiàn)了超時控制的,看如下代碼:
package main
import (
"context"
"io"
"log"
"net/http"
"time"
)
func main() {
req, err := http.NewRequest(http.MethodGet, "https://www.baidu.com", nil)
if err != nil {
log.Fatal(err)
}
// 構(gòu)造一個超時間為50毫秒的Context
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
req = req.WithContext(ctx)
c := &http.Client{}
res, err := c.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
out, err := io.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
log.Println(string(out))
}執(zhí)行后會輸出“context deadline exceeded”,如果將context.WithTimeout方法的timeout參數(shù)調(diào)大一些,就可以看到正常的返回數(shù)據(jù)。
上下文傳遞數(shù)據(jù)
這個作用在鏈路追蹤中非常重要,鏈路追蹤需要將traceID層層往下傳遞,在服務(wù)間傳遞。
type traceIdKey struct{}{}
// 定義固定的Key
var TraceIdKey = traceIdKey{}
func ServeHTTP(w http.ResponseWriter, req *http.Request){
// 首先從請求中獲取到traceID
traceId := getTraceIdFromRequest(req)
// 將Key存入Context中
ctx := context.WithValue(req.Context(), TraceIdKey, traceId)
// 設(shè)置超時時間
ctx = context.WithTimeout(ctx, time.Second)
// 攜帶traceId發(fā)起rpc請求
repResp := RequestRPC(ctx, ...)
// 攜帶traceId查詢DB
dbResp := RequestDB(ctx, ...)
// ...
}
func RequestRPC(ctx context.Context, ...) interface{} {
// 獲取traceid,在調(diào)用rpc時記錄日志
traceId, _ := ctx.Value(TraceIdKey)
// 發(fā)起請求
// ...
return
}接收到請求后,通過req獲取到traceId并記錄到Context中,在調(diào)用其他RPC服務(wù)和查詢DB時,傳入構(gòu)造的Context。在后續(xù)代碼中,可以通過Context拿到存入的traceId。
以上就是Golang中context包使用場景和示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Golang context包使用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang實現(xiàn)數(shù)據(jù)結(jié)構(gòu)Stack(堆棧)的示例詳解
在計算機科學(xué)中,stack(棧)是一種基本的數(shù)據(jù)結(jié)構(gòu),它是一種線性結(jié)構(gòu),具有后進先出(Last In First Out)的特點。本文將通過Golang實現(xiàn)堆棧,需要的可以參考一下2023-04-04
golang連接mysql數(shù)據(jù)庫操作使用示例
這篇文章主要為大家介紹了golang連接mysql數(shù)據(jù)庫操作使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04
golang NewRequest/gorequest實現(xiàn)http請求的示例代碼
本文主要介紹了golang NewRequest/gorequest實現(xiàn)http請求的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
goland?-sync/atomic原子操作小結(jié)
這篇文章主要介紹了goland?-sync/atomic原子操作,原子操作能夠保證執(zhí)行期間是連續(xù)且不會被中斷(變量不會被其他修改,mutex可能存在被其他修改的情況),本文給大家介紹的非常詳細(xì),需要的朋友參考下2022-08-08
GO將mysql?中?decimal?數(shù)據(jù)類型映射到?protobuf的操作方法
這篇文章主要介紹了go如何優(yōu)雅地將?mysql?中?decimal?數(shù)據(jù)類型映射到?protobuf,本文主要展示一下在 protobuf中 float與double的一個區(qū)別,結(jié)合實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09
Golang?使用os?庫的?ReadFile()?讀文件最佳實踐
這篇文章主要介紹了Golang使用os庫的ReadFile()讀文件最佳實踐,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09
golang interface判斷為空nil的實現(xiàn)代碼
這篇文章主要介紹了golang interface判斷為空nil的實現(xiàn)代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04

