Обработка Unix-сигналов в Go

Beginner

This tutorial is from open-source community. Access the source code

Введение

Лаборатория Сигналов демонстрирует, как обрабатывать Unix-сигналы в программах на Go с использованием каналов.

Сигналы

В некоторых случаях мы хотим, чтобы наши программы на Go умело обрабатывали Unix-сигналы. Например, мы можем захотеть, чтобы сервер прекращал работу gracefully, когда получает SIGTERM, или чтобы командная строка прекращала обрабатывать ввод, если получает SIGINT.

  • Создайте буферизованный канал для приема уведомлений о os.Signal.
  • Зарегистрируйте канал для приема уведомлений о заданных сигналах с использованием signal.Notify.
  • Создайте goroutine для выполнения блокирующего приема сигналов.
  • Выведите полученный сигнал и уведомите программу о том, что она может завершиться.
  • Подождите ожидаемый сигнал и затем выйдите.
## Когда мы запускаем эту программу, она будет заблокирована, ожидая сигнала.
## Нажав `ctrl-C` (что в терминале отображается как `^C`), мы можем отправить сигнал `SIGINT`,
## что вызовет вывод программы `interrupt` и затем выход.
$ go run signals.go
awaiting signal
^C
interrupt
exiting

Ниже представлен полный код:

// Иногда мы хотим, чтобы наша программа на Go умело обрабатывала
// [Unix-сигналы](https://en.wikipedia.org/wiki/Unix_signal).
// Например, мы можем захотеть, чтобы сервер прекращал работу gracefully,
// когда получает `SIGTERM`, или чтобы командная строка прекращала обрабатывать ввод,
// если получает `SIGINT`. Вот, как обрабатывать сигналы в Go с использованием каналов.

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
)

func main() {

    // Работа с сигналами в Go осуществляется путем отправки значений `os.Signal`
    // по каналу. Мы создадим канал для приема этих уведомлений. Обратите внимание,
    // что этот канал должен быть буферизованным.
    sigs := make(chan os.Signal, 1)

    // `signal.Notify` регистрирует заданный канал для приема уведомлений
    // о заданных сигналах.
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    // Мы могли бы получать из `sigs` здесь в основной функции,
    // но давайте посмотрим, как это можно сделать в отдельной goroutine,
    // чтобы продемонстрировать более реалистичную ситуацию graceful shutdown.
    done := make(chan bool, 1)

    go func() {
        // Эта goroutine выполняет блокирующий прием сигналов.
        // Когда она получает сигнал, она выводит его и затем уведомляет программу
        // о том, что она может завершиться.
        sig := <-sigs
        fmt.Println()
        fmt.Println(sig)
        done <- true
    }()

    // Программа будет ждать здесь, пока не получит ожидаемый сигнал
    // (как показано выше, когда goroutine отправляет значение по `done`),
    // а затем выйдет.
    fmt.Println("awaiting signal")
    <-done
    fmt.Println("exiting")
}

Резюме

Лаборатория Сигналов демонстрирует, как обрабатывать Unix-сигналы в программах на Go с использованием каналов. Создав буферизованный канал для приема уведомлений о os.Signal и зарегистрировав канал для приема уведомлений о заданных сигналах с использованием signal.Notify, мы можем优雅но обрабатывать сигналы и выходить из программы, когда получаем ожидаемый сигнал.