In Go wordt panic gebruikt om aan te geven dat er een fatale fout in het programma is. Na het aanroepen van panic begint de uitvoering met het unwinden van de stack en het aanroepen van alle deferred-functies. Als er in de stack een functie is met een aanroep van recover(), kan deze de panic opvangen en verwerken — maar recover werkt alleen binnen zijn eigen goroutine en alleen wanneer deze vanuit defer wordt aangeroepen.
De aanbevolen patroon voor veilig herstel:
func safe(fn func()) { defer func() { if r := recover(); r != nil { fmt.Println("Hersteld van panic:", r) } }() fn() } go safe(func() { panic("fail!") // stopt het programma niet })
Het is belangrijk om te onthouden dat:
panic beëindigt de uitvoering van de huidige goroutine tot aan de eerste recoverKan recover een panic opvangen die in een andere goroutine is opgetreden?
Antwoord: Nee, recover werkt alleen binnen de goroutine waar de panic heeft plaatsgevonden, en alleen als het is aangeroepen in een deferred functie. Als de panic in de ene goroutine is opgetreden en recover wordt in een andere aangeroepen, zal er geen opvangen plaatsvinden en zal het programma crashen.
Voorbeeld:
func main() { go func() { panic("binnen goroutine") }() time.Sleep(time.Second) recover() // Werkt niet! Panic beëindigt het programma }
Verhaal
Een ontwikkelaar implementeerde foutafhandelings met behulp van het patroon defer recover() alleen in de hoofdfunctie. Toen er een panic optrad binnen een worker in een aparte goroutine, crashte het hele programma omdat de fout niet werd opgevangen door de globale recover.
Verhaal
In een project werd defer/recover gebruikt voor een gracefull shutdown van de webserver, in de veronderstelling dat dit voldoende was. Het bleek dat een panic in een van de goroutines de hele process beëindigde, omdat de verwerking van recover niet op de juiste plaats stond — het was nodig om de gehele worker pool opnieuw te ontwerpen.
Verhaal
In de berichtverwerkingsstroom ontstond een panic vanwege een fout in de gebruikersfunctie. De ontwikkelaar verwachtte dat deze zou worden "opgevangen" door de bovenliggende recover, maar realiseerde zich niet dat recover ALLEEN werkt vanuit defer. Als gevolg hiervan crashte de service onregelmatig wanneer er een onjuist bericht in de wachtrij verscheen.