Грациозное выключение

Грациозное завершение работы программы включает в себя

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

  • Завершение текущих/завершенных задач (данные в полете)

  • Освободить ресурсы (блокировки файлов, память) и завершить работу (со статусом завершения 0).

Процесс должен быть устойчив к внезапной смерти (например, отключение питания, аппаратные сбои). Для таких сценариев рекомендуется использовать надежную очередь сообщений (beanstalkd).

Концепция очереди

  • производитель помещает задание в очередь (например, json, xml)
  • потребитель следит за очередью и принимает/резервирует доступное задание
  • потребитель удаляет задание из очереди

Утилизация чтения

Пример Graceful shutdown для golang
https://pkg.go.dev/os/signal#Notify

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    // Hello world, the web server
    helloHandler := func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "You requessted %s - %s", req.URL, req.Method)
        fmt.Println("Serving requests from hey...")
        time.Sleep(time.Second * 2)
        defer fmt.Println("remaining")  // acts as remaining requests 
    }
    api := &http.Server{
        Addr:           ":8080",
        Handler:        http.HandlerFunc(helloHandler),
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }

    serverErrors := make(chan error, 1)
    shutdown := make(chan os.Signal, 1)
    go func() {
        log.Printf("main api listening on %s", api.Addr)
        serverErrors <- api.ListenAndServe()
    }()

    signal.Notify(shutdown, os.Interrupt, syscall.SIGTERM)
    //above is not blocking operation although waiting on shutdown channel
    select {
    case err := <-serverErrors:
        log.Fatalf("Error while listening and starting http server: %v", err)

    case <-shutdown:
        log.Println("main: Starting shutdown")
        const timeout = 5 * time.Second
        // Context - it is used for Cancellation and propagation, the context.Background() gives empty context
        ctx, cancel := context.WithTimeout(context.Background(), timeout)
        defer cancel()
        err := api.Shutdown(ctx)
        /*Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners, then closing all idle connections, and then waiting indefinitely for connections to return to idle and then shut down.If the provided context expires before the shutdown is complete, Shutdown returns the context's error, otherwise it returns any error returned from closing the Server's underlying Listener(s).*/
        if err != nil {
            log.Printf("main: Graceful shutdown didnot complete in %v:%v", timeout, err)
            err = api.Close()
            //Close() immediately closes all active net.Listeners and any connections in state StateNew, StateActive, or StateIdle. For a graceful shutdown, use Shutdown.
        }
        if err != nil {
            log.Fatalf("main: could not stop server gracefully Error: %v", err)
        }
    }
}


Вход в полноэкранный режим Выход из полноэкранного режима


дополнительное чтение: ссылка1 (для контекстных таймаутов)

Оцените статью
devanswers.ru
Добавить комментарий