目錄
- Go 協(xié)程超時控制
- Select 超時控制
Go 協(xié)程超時控制
先說個場景:
假設(shè)業(yè)務(wù)中 A 服務(wù)需要調(diào)用 服務(wù)B,要求設(shè)置 5s 超時,那么如何優(yōu)雅實現(xiàn)?
Select 超時控制
考慮是否可以用 select + time.After 方式進行實現(xiàn)
這里主要利用的是通道在攜程之間通信的特點,當程序調(diào)用成功后,會向通道中發(fā)送信號。沒調(diào)用成功前,通道會阻塞。
select {
case res := -c2:
fmt.Println(res)
case -time.After(time.Second * 3):
fmt.Println("timeout 2")
}
當 c2 通道中有數(shù)據(jù)時,并且超時時間沒有達到 3s,走 case res := -c2 這個業(yè)務(wù)邏輯,當超時時間達到 3s , 走的 case -time.After(time.Second * 3) 這個業(yè)務(wù)邏輯, 這樣就可以實現(xiàn)超時 3s 的控制。
res:= -c2 是因為channel 可以實現(xiàn)阻塞,那么 time.After 為啥可以阻塞呢?
看 After 源碼。sleep.go 可以看到其實也是 channel
func After(d Duration) -chan Time {
return NewTimer(d).C
}
完整代碼示例:
package timeout
import (
"fmt"
"testing"
"time"
)
func TestSelectTimeOut(t *testing.T) {
// 在這個例子中, 假設(shè)我們執(zhí)行了一個外部調(diào)用, 2秒之后將結(jié)果寫入c1
c1 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
c1 - "result 1"
}()
// 這里使用select來實現(xiàn)超時, `res := -c1`等待通道結(jié)果,
// `- Time.After`則在等待1秒后返回一個值, 因為select首先
// 執(zhí)行那些不再阻塞的case, 所以這里會執(zhí)行超時程序, 如果
// `res := -c1`超過1秒沒有執(zhí)行的話
select {
case res := -c1:
fmt.Println(res)
case -time.After(time.Second * 1):
fmt.Println("timeout 1")
}
// 如果我們將超時時間設(shè)為3秒, 這個時候`res := -c2`將在
// 超時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")
}
}
運行結(jié)果:
=== RUN TestSelectTimeOut
timeout 1
result 2
--- PASS: TestSelectTimeOut (3.00s)
PASS
go timer 計時器
這個是 timer 類似的計時器實現(xiàn),通用也是通過通道來發(fā)送數(shù)據(jù)。
package main
import "time"
import "fmt"
func main() {
// Ticker使用和Timer相似的機制, 同樣是使用一個通道來發(fā)送數(shù)據(jù)。
// 這里我們使用range函數(shù)來遍歷通道數(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 操作,開始從當前連接中讀取網(wǎng)絡(luò)請求,每當讀取到一個請求則會將該cancelCtx傳入,用以傳遞取消信號,可發(fā)送取消信號,取消所有進行中的網(wǎng)絡(luò)請求。
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è)置了過期時間,當DoHttp(timeoutCtx, info.req) 處理時間超過超時時間時,會自動截止,并且打印 context deadline exceeded。
看個代碼:
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")
}
}
運行結(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 被取消的時間,也就是完成工作的截止日期;
- Done — 返回一個 Channel,這個 Channel 會在當前工作完成或者上下文被取消之后關(guān)閉,多次調(diào)用 Done 方法會返回同一個 Channel;
- Err — 返回 context.Context 結(jié)束的原因,它只會在 Done 返回的 Channel 被關(guān)閉時才會返回非空的值;
- 如果 context.Context 被取消,會返回 Canceled 錯誤;
- 如果 context.Context 超時,會返回 DeadlineExceeded 錯誤;
- Value — 從 context.Context 中獲取鍵對應(yīng)的值,對于同一個上下文來說,多次調(diào)用 Value 并傳入相同的 Key 會返回相同的結(jié)果,該方法可以用來傳遞請求特定的數(shù)據(jù);
到此這篇關(guān)于Go 協(xié)程超時控制的實現(xiàn)的文章就介紹到這了,更多相關(guān)Go 協(xié)程超時控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- 一文搞懂如何實現(xiàn)Go 超時控制
- GoLang之使用Context控制請求超時的實現(xiàn)
- Go語言利用time.After實現(xiàn)超時控制的方法詳解
- 詳解Golang 中的并發(fā)限制與超時控制