Maison >développement back-end >Golang >Explication détaillée de la façon d'implémenter le contrôle du délai d'attente Go
comme exemple. Peu importe à quoi elle est utilisée, comme son nom l'indique, son traitement peut être plus lent. hardWork
func hardWork(job interface{}) error { time.Sleep(time.Minute) return nil}func requestWork(ctx context.Context, job interface{}) error { return hardWork(job)}À ce moment, le client verra toujours l'image familière La plupart des utilisateurs ne regarderont pas le chrysanthème pendant une minute et abandonneront tôt Si vous partez, vous vous retrouverez avec un tas d'occupations de ressources sur l'ensemble du lien d'appel. Cet article n'entrera pas dans d'autres détails et se concentrera uniquement sur la mise en œuvre du délai d'attente. Voyons comment implémenter le délai d'attente et quels sont les pièges.
func requestWork(ctx context.Context, job interface{}) error { ctx, cancel := context.WithTimeout(ctx, time.Second*2) defer cancel() done := make(chan error) go func() { done Allons. écrivez une fonction principale pour la tester<p></p><pre class="brush:php;toolbar:false">func main() { const total = 1000 var wg sync.WaitGroup wg.Add(total) now := time.Now() for i := 0; i Exécutez-la pour voir l'effet<p></p><pre class="brush:php;toolbar:false">➜ go run timeout.go elapsed: 2.005725931sLe délai d'attente a pris effet. Mais est-ce fait ?
time.Sleep(time.Minute*2)fmt.Println("number of goroutines:", runtime.NumGoroutine())sleep 2 minutes, c'est attendre que tout se termine. La tâche se termine, puis nous imprimons le nombre actuel de goroutines. Exécutons-le et voyons le résultat
➜ go run timeout.go elapsed: 2.005725931s number of goroutines: 1001La goroutine a fui, voyons pourquoi cela se produit ? Tout d'abord, la fonction
se termine après 2 secondes d'expiration. Une fois la fonction requestWork
terminée, alors requestWork
n'aura pas de goroutine pour la recevoir. Lorsque la ligne de code done channel
sera exécutée, elle le sera. bloqué et ne peut pas être écrit. Dans ce cas, chaque demande de délai d'attente occupera toujours une goroutine. Lorsque les ressources sont épuisées, l'ensemble du service ne répond plus. done
sur 1 lorsque make chan
, comme suit : buffer size
done := make(chan error, 1)De cette façon,
peut écrire sans bloquer la goroutine, qu'elle expire ou non. À ce stade, quelqu'un peut vous demander s'il y aura un problème si vous écrivez sur un canal qui n'a pas de goroutine pour le recevoir. Dans Go, le canal n'est pas comme nos descripteurs de fichiers courants. Il n'a pas besoin d'être fermé, il l'est. juste un objet. done Il sert uniquement à indiquer au destinataire qu'il n'y a rien à écrire, et n'a pas d'autre but. <code>close(channel)
➜ go run timeout.go elapsed: 2.005655146s number of goroutines: 1Le problème de fuite de goroutine est résolu !
en hardWork
panic("oops")Modifions la fonction
ainsi que le code à attraper l'exception comme suit : main
go func() { defer func() { if p := recover(); p != nil { fmt.Println("oops, panic") } }() defer wg.Done() requestWork(context.Background(), "any")}()Si vous l'exécutez à ce moment, vous constaterez que la panique ne peut pas être capturée. La raison est que la panique générée dans la goroutine à l'intérieur de
ne peut pas être capturée par d'autres goroutines. . requestWork
à requestWork
De même, le panicChan
de panicChan
doit être 1, comme suit : buffer size
.
func requestWork(ctx context.Context, job interface{}) error { ctx, cancel := context.WithTimeout(ctx, time.Second*2) defer cancel() done := make(chan error, 1) panicChan := make(chan interface{}, 1) go func() { defer func() { if p := recover(); p != nil { panicChan <p>改完就可以在 <code>requestWork</code> 的调用方处理 <code>panic</code> 了。</p><h2> <span class="header-link octicon octicon-link"></span>超时时长一定对吗?</h2><p>上面的 <code>requestWork</code> 实现忽略了传入的 <code>ctx</code> 参数,如果 <code>ctx</code> 已有超时设置,我们一定要关注此传入的超时是不是小于这里给的2秒,如果小于,就需要用传入的超时,<code>go-zero/core/contextx</code> 已经提供了方法帮我们一行代码搞定,只需修改如下:</p><pre class="brush:php;toolbar:false">ctx, cancel := contextx.ShrinkDeadline(ctx, time.Second*2)
这里 requestWork
只是返回了一个 error
参数,如果需要返回多个参数,那么我们就需要注意 data race
,此时可以通过锁来解决,具体实现参考 go-zero/zrpc/internal/serverinterceptors/timeoutinterceptor.go
,这里不做赘述。
package mainimport ( "context" "fmt" "runtime" "sync" "time" "github.com/tal-tech/go-zero/core/contextx")func hardWork(job interface{}) error { time.Sleep(time.Second * 10) return nil}func requestWork(ctx context.Context, job interface{}) error { ctx, cancel := contextx.ShrinkDeadline(ctx, time.Second*2) defer cancel() done := make(chan error, 1) panicChan := make(chan interface{}, 1) go func() { defer func() { if p := recover(); p != nil { panicChan <h2> <span class="header-link octicon octicon-link"></span>更多细节</h2><p>请参考 <code>go-zero</code> 源码:</p>
go-zero/core/fx/timeout.go
go-zero/zrpc/internal/clientinterceptors/timeoutinterceptor.go
go-zero/zrpc/internal/serverinterceptors/timeoutinterceptor.go
github.com/tal-tech/go-zero
欢迎使用 go-zero
并 star 支持我们!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!