Go HTTP sunucusunda panic'i recovery middleware ile nasıl yönetirim?
Soru
Go ile yazılı HTTP API'mizde nil pointer dereference yüzünden beklenmedik bir panic oluştu. Bu panic tüm sürecin aniden kapanmasına ve o an içeride olan tüm HTTP isteklerinin drop edilmesine sebep oldu. Go'nun `recover()` mekanizmasıyla panic'leri yakalayan, loglayan ve istemciye zarif bir 500 dönen merkezi bir middleware'i nasıl kurarım?
Cevap
Kısa cevap: Tek bir handler’daki recover edilmemiş panic, tüm süreci çökertir ve o an içerideki bütün istekleri drop eder. Çözüm, her isteği bir recovery middleware’iyle sarmak — böylece bir isteğin panic’i o isteğe hapsolur.
Asıl mesele şu: Go’da bir goroutine panic’lerse ve kimse recover etmezse, program tamamen kapanır. Bir nil deref yüzünden binlerce sağlıklı istek de sizinle birlikte ölür.
- Her isteği
defer recover()ile sarın. Middleware içindedefer func(){ if r := recover(); r != nil { /* logla + 500 yaz */ } }()kurun. Böylece tek bir istekteki panic yakalanır, süreç ayakta kalır ve diğer istekler etkilenmez. - Bu middleware’i zincirin en dışına koyun. Recovery, en dıştaki (outermost) katman olmalı ki kendisinden sonra gelen tüm handler ve middleware’lerin panic’lerini sarsın. İçeride kalırsa, dışarıdaki bir panic’i kaçırırsınız.
- Stack’i + request id’yi loglayın, ama içini sızdırmayın.
debug.Stack()ile stack trace’i ve isteğin id’sini loglayın; istemciye ise generic bir500 Internal Server Errordönün. Hata detayını, dosya yollarını, stack’i kullanıcıya göstermeyin. - Panic’i görünür kılın — bir metrik basın. Her recover’da bir sayaç/metrik artırın. Panic’in sessizce yutulması, recover edilmemesi kadar tehlikelidir; recover edip metriği unutursanız aynı bug günlerce gizli kalır.
recoveryalnızca aynı goroutine’i yakalar — bunu unutmayın. Handler içindego func(){...}()açıyorsanız, o goroutine’deki panic sizin middleware’inizin recover’ına takılmaz ve yine tüm süreci öldürür. Açtığınız her goroutine’in kendi recover’ı olmalı.
Sonuç: Ben olsam istek başına recover middleware + her goroutine için ayrı recover + metrik kurardım. İki uyarı: recover, beklenmedik bug’lar (nil deref) içindir, hata dönmenin yerine geçmez — beklenen hataları error ile dönün. Ve buna rağmen süreci yeniden başlatan bir supervisor tutun. HTTP servisini Go standart kütüphanesiyle yazmanın detayını hub’daki yazıda anlatıyorum.
İlgili Yazılar
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.