Go'da context ile iptal ve zaman aşımı

1 dk okumaBu yazının İngilizcesi de var →

Test edildiği sürüm: Go 1.26

İçindekiler
  1. Context’i her çağrıya aktarın
  2. Zaman aşımını çağıran taraf belirlesin
  3. Goroutine içinde ctx.Done() dinleyin

Bir HTTP isteği iptal edildiğinde, o istek için başlatılan veritabanı sorgusunun ve dış servis çağrılarının da durması gerekir. Go’da bunu sağlayan mekanizma context.Context.

Context’i her çağrıya aktarın

En sık gördüğüm hata, handler içinde context.Background() kullanmak:

handler.go
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
// Yanlış: istemci bağlantıyı kapatsa bile sorgu devam eder.
user, err := h.repo.Find(context.Background(), r.PathValue("id"))
// ...
}

Doğrusu isteğin kendi context’ini kullanmak:

handler.go
user, err := h.repo.Find(r.Context(), r.PathValue("id"))

Zaman aşımını çağıran taraf belirlesin

Dış servise giden her çağrının bir üst sınırı olmalı. Bu sınırı çağrının yapıldığı yerde tanımlamak, okuyan kişinin davranışı tek bakışta anlamasını sağlar.

ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
resp, err := h.payments.Charge(ctx, req)
if errors.Is(err, context.DeadlineExceeded) {
http.Error(w, "ödeme servisi yanıt vermedi", http.StatusGatewayTimeout)
return
}

defer cancel() satırını atlamak, zaman aşımı dolana kadar kaynakların serbest bırakılmamasına neden olur.

İpucu

go vet içindeki lostcancel kontrolü, çağrılmayan cancel fonksiyonlarını yakalar. CI’da çalıştırmaya değer.

Goroutine içinde ctx.Done() dinleyin

Arka planda çalışan bir goroutineGoroutine Go çalışma zamanının yönettiği, işletim sistemi thread’inden çok daha hafif eşzamanlı çalışma birimi. Tüm terimler →, context iptal edildiğinde çıkmalı:

for {
select {
case <-ctx.Done():
return ctx.Err()
case msg := <-messages:
process(msg)
}
}

Aşağıdaki örneği çalıştırıp zaman aşımının nasıl işlediğini görebilirsiniz:

package main
import (
"context"
"fmt"
"time"
)
func slowQuery(ctx context.Context) error {
select {
case <-time.After(2 * time.Second):
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
err := slowQuery(ctx)
fmt.Println("sonuç:", err) // sonuç: context deadline exceeded
}

Go Playground'da çalıştır ↗

Sızıntıları yakalamak için testlerde goleak kullanmak iyi bir alışkanlık:

GitHububer-go/goleakGoroutine leak detector★ 5,3 B yıldız · Go
Değişiklik geçmişi (1)
  1. Alt bilgiye build imzası, 404 ve çevrimdışı sayfalarına terminal görünümü ekleae75462

Klavye kısayolları

Arama ve komutlar
K
Ara
/
Ana sayfa
gh
Yazılar
gy
Notlar
gn
Kod parçacıkları
gk
Arşiv
ga
Projeler
gp
Sonraki yazı
j
Önceki yazı
k
Temayı değiştir
t
Bu listeyi göster
?