主頁(yè) > 知識(shí)庫(kù) > Go 協(xié)程超時(shí)控制的實(shí)現(xiàn)

Go 協(xié)程超時(shí)控制的實(shí)現(xiàn)

熱門標(biāo)簽:百度地圖標(biāo)注位置網(wǎng)站 武漢百應(yīng)人工智能電銷機(jī)器人 智能語(yǔ)音電銷的機(jī)器人 400手機(jī)電話免費(fèi)辦理 揚(yáng)州電銷外呼系統(tǒng)軟件 如何利用高德地圖標(biāo)注家 開通400電話申請(qǐng)流程 上海企業(yè)外呼系統(tǒng)排名 電腦外呼系統(tǒng)輻射大嗎

Go 協(xié)程超時(shí)控制

  • Select 阻塞方式
  • Context 方式

先說(shuō)個(gè)場(chǎng)景:

假設(shè)業(yè)務(wù)中 A 服務(wù)需要調(diào)用 服務(wù)B,要求設(shè)置 5s 超時(shí),那么如何優(yōu)雅實(shí)現(xiàn)?

Select 超時(shí)控制

考慮是否可以用 select + time.After 方式進(jìn)行實(shí)現(xiàn)

這里主要利用的是通道在攜程之間通信的特點(diǎn),當(dāng)程序調(diào)用成功后,會(huì)向通道中發(fā)送信號(hào)。沒調(diào)用成功前,通道會(huì)阻塞。

select {
 case res := -c2:
  fmt.Println(res)
 case -time.After(time.Second * 3):
  fmt.Println("timeout 2")
 }

當(dāng) c2 通道中有數(shù)據(jù)時(shí),并且超時(shí)時(shí)間沒有達(dá)到 3s,走 case res := -c2 這個(gè)業(yè)務(wù)邏輯,當(dāng)超時(shí)時(shí)間達(dá)到 3s , 走的 case -time.After(time.Second * 3) 這個(gè)業(yè)務(wù)邏輯, 這樣就可以實(shí)現(xiàn)超時(shí) 3s 的控制。

res:= -c2 是因?yàn)閏hannel 可以實(shí)現(xiàn)阻塞,那么 time.After 為啥可以阻塞呢?

看 After 源碼。sleep.go 可以看到其實(shí)也是 channel

func After(d Duration) -chan Time {
 return NewTimer(d).C
}

完整代碼示例:

package timeout

import (
 "fmt"
 "testing"
 "time"
)

func TestSelectTimeOut(t *testing.T) {
 // 在這個(gè)例子中, 假設(shè)我們執(zhí)行了一個(gè)外部調(diào)用, 2秒之后將結(jié)果寫入c1
 c1 := make(chan string, 1)
 go func() {
  time.Sleep(time.Second * 2)
  c1 - "result 1"
 }()
 // 這里使用select來(lái)實(shí)現(xiàn)超時(shí), `res := -c1`等待通道結(jié)果,
 // `- Time.After`則在等待1秒后返回一個(gè)值, 因?yàn)閟elect首先
 // 執(zhí)行那些不再阻塞的case, 所以這里會(huì)執(zhí)行超時(shí)程序, 如果
 // `res := -c1`超過(guò)1秒沒有執(zhí)行的話
 select {
 case res := -c1:
  fmt.Println(res)
 case -time.After(time.Second * 1):
  fmt.Println("timeout 1")
 }
 // 如果我們將超時(shí)時(shí)間設(shè)為3秒, 這個(gè)時(shí)候`res := -c2`將在
 // 超時(shí)case之前執(zhí)行, 從而能夠輸出寫入通道c2的值
 c2 := make(chan string, 1)
 go func() {
  time.Sleep(time.Second * 2)
  c2 - "result 2"
 }()
 select {
 case res := -c2:
  fmt.Println(res)
 case -time.After(time.Second * 3):
  fmt.Println("timeout 2")
 }
}

運(yùn)行結(jié)果:

=== RUN   TestSelectTimeOut
timeout 1
result 2
--- PASS: TestSelectTimeOut (3.00s)
PASS

go timer 計(jì)時(shí)器

這個(gè)是 timer 類似的計(jì)時(shí)器實(shí)現(xiàn),通用也是通過(guò)通道來(lái)發(fā)送數(shù)據(jù)。

package main
import "time"
import "fmt"
func main() {
  // Ticker使用和Timer相似的機(jī)制, 同樣是使用一個(gè)通道來(lái)發(fā)送數(shù)據(jù)。
  // 這里我們使用range函數(shù)來(lái)遍歷通道數(shù)據(jù), 這些數(shù)據(jù)每隔500毫秒被
  // 發(fā)送一次, 這樣我們就可以接收到
  ticker := time.NewTicker(time.Millisecond * 500)
  go func() {
    for t := range ticker.C {
    fmt.Println("Tick at", t)
    }
  }()
  // Ticker和Timer一樣可以被停止。 一旦Ticker停止后, 通道將不再
  // 接收數(shù)據(jù), 這里我們將在1500毫秒之后停止
  time.Sleep(time.Millisecond * 1500)
  ticker.Stop()
  fmt.Println("Ticker stopped")
}

go context

context 監(jiān)聽是否有 IO 操作,開始從當(dāng)前連接中讀取網(wǎng)絡(luò)請(qǐng)求,每當(dāng)讀取到一個(gè)請(qǐng)求則會(huì)將該cancelCtx傳入,用以傳遞取消信號(hào),可發(fā)送取消信號(hào),取消所有進(jìn)行中的網(wǎng)絡(luò)請(qǐng)求。

  go func(ctx context.Context, info *Info) {
   timeLimit := 120
   timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(timeLimit)*time.Millisecond)
   defer func() {
    cancel()
    wg.Done()
   }()
   resp := DoHttp(timeoutCtx, info.req)
  }(ctx, info)

關(guān)鍵看業(yè)務(wù)代碼: resp := DoHttp(timeoutCtx, info.req) 業(yè)務(wù)代碼中包含 http 調(diào)用 NewRequestWithContext

req, err := http.NewRequestWithContext(ctx, "POST", url, strings.NewReader(paramString))

上面的代碼,設(shè)置了過(guò)期時(shí)間,當(dāng)DoHttp(timeoutCtx, info.req) 處理時(shí)間超過(guò)超時(shí)時(shí)間時(shí),會(huì)自動(dòng)截止,并且打印 context deadline exceeded。

看個(gè)代碼:

package main

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

func TestTimerContext(t *testing.T) {
 now := time.Now()
 later, _ := time.ParseDuration("10s")

 ctx, cancel := context.WithDeadline(context.Background(), now.Add(later))
 defer cancel()
 go Monitor(ctx)

 time.Sleep(20 * time.Second)

}

func Monitor(ctx context.Context) {
 select {
 case -ctx.Done():
  fmt.Println(ctx.Err())
 case -time.After(20 * time.Second):
  fmt.Println("stop monitor")
 }
}

運(yùn)行結(jié)果:

=== RUN   TestTimerContext
context deadline exceeded
--- PASS: TestTimerContext (20.00s)
PASS

Context 接口有如下:

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() -chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
  • Deadline — 返回 context.Context 被取消的時(shí)間,也就是完成工作的截止日期;
  • Done — 返回一個(gè) Channel,這個(gè) Channel 會(huì)在當(dāng)前工作完成或者上下文被取消之后關(guān)閉,多次調(diào)用 Done 方法會(huì)返回同一個(gè) Channel;
  • Err — 返回 context.Context 結(jié)束的原因,它只會(huì)在 Done 返回的 Channel 被關(guān)閉時(shí)才會(huì)返回非空的值;
    • 如果 context.Context 被取消,會(huì)返回 Canceled 錯(cuò)誤;
    • 如果 context.Context 超時(shí),會(huì)返回 DeadlineExceeded 錯(cuò)誤;
  • Value — 從 context.Context 中獲取鍵對(duì)應(yīng)的值,對(duì)于同一個(gè)上下文來(lái)說(shuō),多次調(diào)用 Value 并傳入相同的 Key 會(huì)返回相同的結(jié)果,該方法可以用來(lái)傳遞請(qǐng)求特定的數(shù)據(jù);

到此這篇關(guān)于Go 協(xié)程超時(shí)控制的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go 協(xié)程超時(shí)控制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • 一文搞懂如何實(shí)現(xiàn)Go 超時(shí)控制
  • GoLang之使用Context控制請(qǐng)求超時(shí)的實(shí)現(xiàn)
  • Go語(yǔ)言利用time.After實(shí)現(xiàn)超時(shí)控制的方法詳解
  • 詳解Golang 中的并發(fā)限制與超時(shí)控制

標(biāo)簽:江西 新余 武漢 延邊 黑龍江 宜賓 張掖 嘉峪關(guān)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Go 協(xié)程超時(shí)控制的實(shí)現(xiàn)》,本文關(guān)鍵詞  協(xié)程,超時(shí),控制,的,實(shí)現(xiàn),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Go 協(xié)程超時(shí)控制的實(shí)現(xiàn)》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于Go 協(xié)程超時(shí)控制的實(shí)現(xiàn)的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章