Предотвращение панических сбоев Golang

В Голанге паника без восстановления приведет к сбою процесса, поэтому в начале каждой функции я добавляю следующий фрагмент кода:

defer func() {
    if err := recover(); err != nil {
        fmt.Println(err)
    }
}()

просто для того, чтобы предотвратить сбой моей программы. Теперь мне интересно, это действительно путь? Потому что мне кажется немного странным размещать везде один и тот же код.

Мне кажется, что в Java пути исключения доходят до вызывающей функции, пока основная функция не станет лучшим способом управления исключениями/паникой. Я понимаю, что это дизайн Go, но в чем преимущество немедленного сбоя процесса, как это делает Go?


person Qian Chen    schedule 18.08.2014    source источник
comment
Вы не должны думать о паниках как о эквиваленте Go для исключений Java. Они используются гораздо реже. В Java они используются для обозначения любых ошибок. С другой стороны, в Go идиома для указания на ошибку заключается в возврате ошибки в качестве последнего возвращаемого значения (например, см. os.Open). Таким образом, паники зарезервированы для случаев, которые должны вызвать сбой программы, например разыменование нулевого указателя.   -  person joshlf    schedule 18.08.2014
comment
(есть некоторые исключения из этого правила — паники могут быть полезны в редких случаях как программная конструкция — но в основном они используются только для неисправимых ошибок)   -  person joshlf    schedule 18.08.2014
comment
Да, но для того, чтобы написать надежный серверный программатор, особенно расширяемый плагин или систему перехватчиков, вы действительно не должны позволять плагину или перехватчику легко ломать ваш основной сервер, я прав?   -  person Qian Chen    schedule 18.08.2014
comment
Правильно, может быть необходимость в таком поведении (net/http Server Go использует recovery для отлова панических горутин), но в то же время вы не спрашивали об этом. Паника/отсрочка/восстановление являются исключением — используйте их только при необходимости и никогда больше.   -  person elithrar    schedule 18.08.2014
comment
Спасибо @elithrar, я понимаю, что должен быть осторожен при использовании panic/recover. Однако, если я воспользуюсь чужой библиотекой, это будет вне моего контроля. Да, чтобы быть в безопасности, я могу отложить/восстановить каждую свою функцию, что я и делаю сейчас. Мне просто интересно, в чем преимущество такого легкого сбоя процесса по сравнению с моделью пузыря Java?   -  person Qian Chen    schedule 18.08.2014
comment
Вы должны думать о восстановлении Go как об эквиваленте в Java перехвата RuntimeException или NullPointerException или чего-то подобного. Вы никогда не увидите этот код в приложении Java.   -  person joshlf    schedule 18.08.2014
comment
@synful, да, согласен, однако в Java RuntimeException или NullPointerException процесс не крашится. Я не вижу никакого преимущества в сбое процесса по сравнению с вызовом вызывающих абонентов до main.   -  person Qian Chen    schedule 18.08.2014
comment
Или, может быть, паника используется неправильно, на самом деле это означает System.exit (1) в Java? Если это так, должен ли сбой сетевого подключения вызывать System.exit(1) в Java?   -  person Qian Chen    schedule 18.08.2014
comment
На данный момент у меня нет простого способа запуска java-кода, но я не верю, что он работает так, как описано. Если у меня есть main(), который порождает поток, и этот поток генерирует исключение, должен ли я верить, что main каким-то образом перехватит исключение? Скорее всего, исключение просто потеряется и никто не узнает. Программа просто будет в нежелательном состоянии. Нам нравятся желаемые состояния.   -  person Dustin    schedule 18.08.2014
comment
В Java порожденный поток не может генерировать исключение, не относящееся к среде выполнения, или код не будет компилироваться, поскольку Runnable.run ничего не объявляет генерировать. Однако можно сгенерировать исключение времени выполнения, создание исключения времени выполнения не приведет к сбою основного потока.   -  person Qian Chen    schedule 19.08.2014


Ответы (2)


Вы должны оправиться от паники, только если точно знаете, почему. Программа Go будет паниковать по существу в двух случаях:

  • Логическая ошибка программы (например, разыменование нулевого указателя или выход за границы массива или доступ к срезу)
  • Преднамеренная паника (вызывается с помощью panic(...)) либо из вашего кода, либо из кода, который вызывает ваш код.

В первом случае уместно сбой, потому что это означает, что ваша программа вошла в плохое состояние и не должна продолжать выполняться. Во втором случае вы должны оправиться от паники только в том случае, если вы этого ожидаете. Лучший способ объяснить это — просто сказать, что это крайне редкое явление, и вы узнаете об этом случае, если увидите его. Я почти уверен, что какой бы код вы ни писали, вам не нужно восстанавливаться после паники.

person joshlf    schedule 18.08.2014
comment
Что я делаю, так это пишу сервер, ожидающий входящих соединений. Когда входящее соединение устанавливается, сервер запускает процедуру go для обработки этого входящего соединения. Я не хочу, чтобы сервер рухнул, если какая-то рутинная паника. Меня даже не волнует, что произошло во время рутины. Если это не удалось, это не удалось. В этом случае, кажется, я должен убедиться, что мне нужно восстановить любую возможную панику в коде подпрограммы go. Я прав, или я что-то упускаю? - person Qian Chen; 18.08.2014
comment
Если ваша программа паникует, это указывает на ошибку программирования. Любая обычная ошибка, которую вы хотели бы проигнорировать (например, тайм-аут сети или плохо отформатированный запрос), придет в виде значения ошибки, возвращаемого функцией, а не паники. - person joshlf; 18.08.2014
comment
Это другой случай: github.com/elgs/gorest/blob/master/data_interceptor .go этот интерфейс перехватчика предназначен для переопределения в будущем. Я думаю, что паника слишком избыточна, чтобы любой мог легко обрушить весь процесс, если только восстановление не будет поставлено везде. - person Qian Chen; 18.08.2014
comment
@ElgsQianChen panic() in go приводит к сбою вашей программы так же, как это произошло бы при разыменовании нулевого указателя в C. В любом случае вы действительно не хотите, чтобы ваша программа продолжала выполняться. - person fuz; 18.08.2014
comment
Я был в такой же ситуации. На моем сервере я получаю данные JSON из WebSocket. Затем я декодирую его и делаю для него утверждения типа, предполагая, что данные верны. При обычном использовании это не может дать сбой, но что, если данные были изменены? Разве восстановление паники не лучший способ убедиться, что хакер (например) не сможет таким образом вывести из строя мой сервер? - person Sebastien C.; 18.08.2014
comment
@ sebcap26 Нет, не паниковать — лучший способ предотвратить сбой вашего сервера. Похоже, вы делаете чрезвычайно сложные и хрупкие вещи со своим вкладом. Делайте менее хрупкие и менее сложные вещи с вашим вводом, и вам будет легче работать, быстрее, безопаснее и без сбоев. - person Dustin; 18.08.2014
comment
@ sebcap26, есть способ делать утверждения типов, не вызывая паники. Если вы сделаете val, ok := interfaceValue.(Type), второе возвращаемое значение из утверждения типа (то есть значение, помещенное в ok) будет логическим, указывающим, успешно ли утверждение. Используя это, если утверждение не удалось, оно просто сделает ok ложным вместо паники. - person joshlf; 18.08.2014
comment
@synful, я думаю, мы говорим о двух разных темах. Да, мы можем попытаться быть достаточно осторожными, чтобы не паниковать. Однако вопрос в том, должна ли паника без восстановления немедленно крашить процесс и почему? Наверное, это сложно реализовать, когда некоторые переменные находятся в куче. Должно быть легко очистить стек, когда он паникует (go)/выдает исключение (java), ему просто нужно просто очистить стек вызовов. Однако данные в куче включают сборщик мусора. Это может быть непросто или может быть дорого. Так что, возможно, дизайнеры Go решили просто свернуть процесс. - person Qian Chen; 19.08.2014
comment
Честно говоря, вопрос не в том, чтобы не паниковать. Если вы пишете программу, которая вызывает панику, и вы не делали этого намеренно (например, вы не вызывали panic(...) сами), в вашей программе есть ошибка. Ни одна программа не должна паниковать, если только она этого явно не хочет. Вы можете думать об этом как о делении на 0 или доступе к памяти, которой вы не владеете в C. Это не ошибка, такая как тайм-аут сети, - это ошибка. Вы бы не хотели восстанавливаться после этого, потому что это означает, что ваша программа делает что-то не так. Это означает, что вам нужно исправить вашу программу. - person joshlf; 19.08.2014
comment
Я бы сказал, что это зависит. Если вы пишете критическую систему, и какая-то подсистема паникует, вы можете восстановиться и попытаться запустить ее снова через 10 секунд. Это было бы полезно в тех случаях, когда продолжение работы системы с ошибками в неизвестном состоянии было бы лучше, чем сбой. Например. в кардиостимуляторе ;-) - person Bjarke Ebert; 29.04.2015

Как правило, даже с исключениями вы ловите их на «FaultBarrier». Обычно это место, где создаются все новые темы. Дело в том, чтобы поймать и зарегистрировать неожиданные сбои.

В Go вы используете возвращаемые значения для всех ожидаемых сбоев. Фреймворк, в котором вы работаете, обычно имеет барьер сбоя для перехвата сеанса (т. е. обычно HTTP-транзакции) и регистрации проблемы. Единственное другое место, где я вижу восстановление, это такие вещи, как неидемпотентная функция Close. Если у вас есть ситуация, когда вы не можете сказать, закрыто ли что-то уже, но знаете, что это должно быть закрыто, вы можете в конечном итоге выполнить восстановление, чтобы вторая паника закрытия была проигнорирована, вместо того, чтобы провалить то, что вы делаете все время. путь до FaultBarrier.

person Rob    schedule 28.08.2016