Нужно, если параметр не 1 , то выйти из функции (но не из приложения вообще). Можно, конечно, поставить else , но везде тогда делать обёртки? Или можно как-то выйти из функции?
Отслеживать
67.8k 210 210 золотых знаков 76 76 серебряных знаков 218 218 бронзовых знаков
задан 14 июл 2015 в 15:14
Rakzin Roman Rakzin Roman
5,670 12 12 золотых знаков 77 77 серебряных знаков 170 170 бронзовых знаков
14 июл 2015 в 15:57
Я наверное не правильно выразился. Есть func Index(w http.ResponseWriter, r *http.Request) < parametr=0 if parametr!=»1″ < fmt.Fprintln(w, «Параметр=1») >fmt.Fprintln(w, «текст после проверки») //Я хочу это не отображать,если parametr!=»1″ > //Нужно,если параметр не 1, то выйти из функции.(но не из приложения вообще) Можно конечно поставить else , но везде тогда делать обёртки? или можно как-то выйти из функции
14 июл 2015 в 16:30
Вы хотите return ?
14 июл 2015 в 16:41
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
выход
Exit causes the current program to exit with the given status code. Conventionally, code zero indicates success, non-zero an error. The program terminates immediately; deferred functions are not run.
Завершаем программы изящно / Graceful Shutdown в #Golang
мой вольный перевод:
Exit вызывает завершение текущей программы с указанным кодом завершения. принято считать, что код 0 означает успешное завершение, а не-нулевой код означает ошибку. программа завершается немедленно, отложенные функции не вызываются.
возврат
A «return» statement in a function F terminates the execution of F, and optionally provides one or more result values. Any functions deferred by F are executed before F returns to its caller.
мой вольный перевод:
оператор «return» в функции «F» прерывает выполнение «F» и (опционально) возвращает один или более результатов. любые функции, «отложенные» функцией «F», выполняются перед тем, как управление вернётся в вызывавшую «F» функцию.
func noResult() < return >func simpleF() int < return 2 >func complexF1() (re float64, im float64)
Источник: ru.stackoverflow.com
Изящное завершение работы в Go
Go широко используется для внутреннего программирования, и его сообщество растет с каждым днем. Лично мне нравится программировать на Go.
Я хотел показать краткое руководство о том, как корректно завершить работу наших приложений Go. , что на самом деле очень простой процесс.
Эта история о том, как реализовать плавное завершение работы в Go с помощью
Установка языка программирования Go, среды разработки. Создание и компиляция первой программы
пошагового руководства.
Итак, в этой истории предполагается, что вы уже знаете, почему нам нужно плавно завершать работу наших приложений. с других языков программирования.
Сигналы
Сигнал — это событие, генерируемое системами UNIX и Linux в ответ на какое-либо условие, и Go может прослушивать эти события на уровне приложения.
Сигналов много, но нас беспокоят только сигналы выключения. Итак, давайте немного посмотрим на них:
SIGTERM отправляется как общий сигнал завершения работы программного обеспечения, который отправляется почти для всех событий завершения работы (кроме тех, что указаны ниже).
SIGKILL отправляется как сигнал завершения, который отправляется для событий « немедленно выйти» и, как правило, не должен мешать с участием.
SIGINT отправляется, когда пользователь вводит сигнал прерывания (например, Ctrl + C).
— аналогично SIGTERM, но для пользовательских событий —
SIGQUIT отправляется, когда пользователь вводит сигнал выхода (Ctrl + D).
— аналогично SIGKILL, но для пользовательских событий (таких как принудительный выход) —
В идеале для производственной среды достаточно прослушивания SIGTERM. (Модули Kubernetes отправляют сигнал SIGTERM при завершении.)
SIGINT безопасен для прослушивания, и это хорошая практика и для локальной отладки.
Итак, SIGINT и SIGTERM охватывают почти все возможные сценарии, пока мы улавливаем и обрабатываем сигналы, наши приложение должно быть в порядке.
Использование каналов для улавливания сигналов
Если вы знакомы с параллелизмом в Go, то вы знаете о каналах.
Если вы не знакомы, то вот мое руководство по теме: Параллелизм в Go.
Что нам здесь нужно, так это иметь блок кода для прослушивания сигнала «выключение» и выполнения нашего пользовательского поведения (освобождение ресурсов) на основе этой информации.
Поскольку каналы ждут (из-за своего поведения блокировки) до получения сообщения, это идеально подходит для этой работы.
Таким образом, мы можем комбинировать каналы с os.Signal, как указано выше:
Когда мы запускаем наше приложение, «Готово!» не будет напечатан, он будет ждать, пока канал gracefulShutdown не получит сообщение.
Поскольку gracefulShutdown прослушивает SIGINT и SIGTERM, наше приложение будет ждать, пока программа не будет завершена.
Итак, когда мы закрываем приложение, на выходе получаем:
Но, как правило, этого недостаточно, нам также необходимо освободить ресурсы из источников, которые мы используем, таких как внешние API, базы данных и т. Д.
Код прямо из документации Go:
Вызов cancel () освобождает ресурсы, связанные с определенным контекстом.
Таким образом, мы можем объединить наш канал постепенного завершения работы с нашим контекстом для достижения желаемого поведения:
Дождитесь сигнала выключения, освободите ресурсы должным образом.
Итак, давайте сделаем это:
Теперь вы можете настроить этот код как хотите, допустим, это почасовая работа, и вы хотите отправить «завершенное» сообщение в очередь сообщений, вы можете сделать это следующим образом:
Все отзывы приветствуются, и я надеюсь, что они были вам полезны. 🙂
Источник: digitrain.ru
Организация «чистого» завершения приложений на Go
Здравствуйте, в данной заметке будет затронута тема организации «чистого» завершения для приложений, написанных на языке Go.
Чистым выходом я называю наличие гарантий того, что в момент завершения процесса (по сигналу или по любым иным причинам кроме system failure), будут выполнены определённые процедуры и выход будет отложен до окончания их выполнения. Далее я приведу несколько типичных примеров, расскажу о стандартном подходе, а также продемонстрирую свой пакет для упрощённого применения этого подхода в ваших программах и сервисах.
1. Введение
Итак, наверняка вы замечали хоть раз, как какой-нибудь сервер или утилита ловит ваш кручёный Ctrl^C и, дико извиняясь конечно, просит подождать, пока она порешает дела, которые никак нельзя отложить. Хорошо написанные программы завершают дела и выходят, плохие же впадают в deadlock и сдаются только при виде SIGKILL . Точнее, о SIGKILL программа узнать не успевает, подробно процесс описан здесь: SIGTERM vs. SIGKILL и Unix Signal.
При переходе на Go в качестве основного языка разработки и после продолжительного использования последнего для написания различных сервисов мне стало ясно, что добавлять обработку сигналов нужно буквально в каждый сервис. В основном из-за того, что в Go многопоточность является примитивом языка. Внутри одного процесса могут одновременно работать, к примеру, следующие потоки:
- Connection pool клиентов БД;
- Consumer для pub/sub очереди;
- Publisher для pub/sub очереди;
- N потоков собственно воркеров;
- Кэш в памяти;
- Открытые файлы логов;
И с запуском всё предельно просто: сначала стартуем пул клиентов БД, если не стартовал — выходим с ошибкой. Затем инициализируем кэш в памяти. Затем запускаем publisher, если не стартовал — выходим с ошибкой. Затем открываем файлы — например логи.
Затем запускаем воркеров, да побольше, которые будут потреблять данные через consumer, писать в БД и что-то держать в кэше, а результаты складывать в publisher. Ах да, ещё события обработки будут писаться в логи, не обязательно из тех же потоков. И, наконец, активируем всё это открыв поток данных consumer, а если не отрылся — выходим.
Инициализация происходит последовательно, в один поток, в случае ошибки на одном этапе откатывать уже выполненные этапы инициализации не обязательно, так как система находится в нулевом положении всё это время, пока не откроем поток данных. И вот открыли поток данных, а через 5 минут нам срочно потребовалось выйти, завершить всё, да так, чтобы красиво и чисто.
Зачем? А потому что не все результаты из буферизированного канала могли успеть быть полученными процессом записи в БД, да и те, что были считаны из канала, могли не успеть дойти до БД по сети. И не все объекты могли успеть опубликоваться в pub/sub очередь. Не все воркеры могли успеть сдать свои результаты в соответствующие каналы.
Потребление очереди воркерами могло быть также буферизировано, а значит, небольшая часть объектов могла оказаться считанной с сервера pub/sub очереди, но ещё не обработанной воркерами. Кэш в памяти, например, должен быть сдамплен на диск в момент завершения программы, а ещё все буферы с данными логов должны быть очищены в соответствующие файлы. Всё это перечислено здесь с целью показать, что любой примитивный сервис с несколькими фоновыми задачами обречён иметь способ надёжного отслеживания выхода приложения. И вовсе не ради красивого уведомления «Bye bye. » в консоли, а как жизненно необходимый механизм синхронизации многопоточного комбайна.
2. Немного практики
В Go имеется хороший инструмент — defer, это выражение, будучи применённым к функции, добавит её в специальный список. Функции из этого списка будут выполнены в обратном порядке перед возвратом из текущей функции. Такой механизм иной раз упрощает работу с мьютексами и прочими ресурсами, которые нужно освободить при возврате. Эффект defer действует даже если случается паника (=исключение), то есть, определённый в deferred-функции код получает гарантию быть выполненным, а сами исключения таким способом могут быть пойманы и обработаны.
func Checked() < defer func() < // проверка, была ли паника if x := recover(); x != nil < // можно написать в лог, а также пробросить исключение наверх >>() // что-нибудь делаем, случается паника >
Но есть один злостный антипаттерн, почему-то зачастую defer начинают использовать в функции main . Например:
func main() < defer doCleanup() // немного псевдоработы fmt.Println(«10 seconds to go. «) time.Sleep(10 * time.Second) >
Код отлично отработает в случае обычного возврата и даже паники, но люди забыли о том, что defer не сработает в случае получения процессом сигнала на завершение (выполняется syscall exit, из документации Go: «The program terminates immediately; deferred functions are not run.»).
Чтобы грамотно обработать подобную ситуацию, сигналы следует ловить вручную «подписавшись» на нужные типы сигналов. Распространённая практика (судя по ответам на StackOverflow) заключается в использовании signal.Notify, паттерн выглядит примерно так:
sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) go func() < s := ()
Для скрытия лишних деталей реализации и был придуман пакет xlab/closer, о нём пойдёт речь дальше.
3. Closer
Итак, пакет closer берёт на себя обязанность отслеживать сигналы, позволяет привязать функции и автоматически выполнит их в обратном порядке при завершении. Пакет потокобезопасен, тем самым избавляя пользователя от необходимости думать о возможных здесь состояниях гонки при вызове closer.Close из нескольких потоков одновременно. API на данный момент состоит из 5 функций: Init, Bind, Checked, Hold и Close. Init позволяет пользователю переопределить список сигналов и другие опции, использование остальных функций рассмотрим на примерах.
Стандартный список сигналов: syscall.SIGINT, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGABRT .
Пример обычный
func main() < closer.Bind(cleanup) go func() < // делаем работу в отдельном потоке fmt.Println(«10 seconds to go. «) time.Sleep(10 * time.Second) // по окончании требуем завершение процесса closer.Close() >() // блокирует, пока не будет отработан выход — по сигналу или через closer.Close closer.Hold() > func cleanup()
Пример с ошибкой
Функция closer.Checked позволяет делать проверку на ошибки и ловить исключения. Здесь код возврата будет отличен от нуля, причём обработкой выхода занимается по-прежнему пакет closer .
func main() < closer.Bind(cleanup) closer.Checked(run, true) >func run() error < fmt.Println(«Will throw an error in 10 seconds. «) time.Sleep(10 * time.Second) return errors.New(«KAWABANGA!») >func cleanup()
Пример с паникой (исключением)
func main() < closer.Bind(cleanup) closer.Checked(run, true) >func run() error < fmt.Println(«Will panic in 10 seconds. «) time.Sleep(10 * time.Second) panic(«KAWABANGA!») return nil >func cleanup()
Таблица соответствия кодов завершения:
Событие | Код завершения ————- | ————- error = nil | 0 (успех) error != nil | 1 (ошибка) panic | 1 (ошибка)
Заключение
Таким образом, вне зависимости от первопричины завершения процесса, ваше приложение на Go отработает необходимую процедуру «чистого» завершения. В Go принято для каждой сущности, требующей такой процедуры, писать метод Close, который бы финализировал все внутренние процессы этой сущности. Значит, завершение вышеописанного сервиса из второй части данной статьи будет заключаться в вызове метода Close() для всех созданных сущностей, в обратном порядке.
Сначала закрывается поток данных consumer очереди pub/sub, новых задач в систему поступать не будет, затем система дождётся, пока все воркеры отработают и завершатся, только после этого будет синхронизирован с диском кэш, закрыт канал записи в БД, закрыт канал publisher, синхронизированы и закрыты файлы логов, и, наконец, будут закрыты подключения к БД и сам publisher. На словах звучит достаточно серьёзно, но на деле же достаточно лишь грамотно написать метод Close каждой сущности и в main при инициализации использовать closer.Bind. Эскиз main для наглядности:
func main() < defer closer.Close() pool, _ := xxx.NewPool() closer.Bind(pool.Close) pub, _ := yyy.NewPublisher() closer.Bind(function()< pub.Stop() ) wChan := make(chan string, BUFFER_SIZE) workers, _ := zzz.NewWorkgroup(pool, pub, wChan) closer.Bind(workers.Close) sub, _ := yyy.NewConsumer() closer.Bind(sub.Stop) // блокирующий вызов (иначе используйте closer.Hold) sub.Consume(wChan) >
Удачной вам синхронизации!
Источник: habr.com