LINUX.ORG.RU

Go best practices. Как сделать graceful restart?

 ,


0

4

Как лучше организовать graceful restart для http-сервер на goalng?
Или как вариант, как лучше организовать структуру приложения, что бы в нем был graceful restart?

И конкретнее:

type App struct {
    // <<<< здесь сложная структура,
    // инициализируется данными из конфига
}

func (app *App) reqHandler(w http.ResponseWriter, r *http.Request) {
  // Обработка входящего http-запроса
}

func main() {

    app := NewApp() //<<<< здесь загрузка из конфига

    r := mux.NewRouter()
    // Собственно вопрос: Можно-ли заменить
    r.HandleFunc("/", app.reqHandler) // <<<< вот этот
    // handler после старта сервера?
    srv := http.Server{
        Handler: r,
        Addr:    ":8080",
    }   
    srv.ListenAndServe()
}

Чтобы сделать graceful restart был в приложении, можно настроить graceful restart в ситемд. В системд graceful restart настраивается так:

[Unit]
Description=graceful restart
After=some.service

[Service]
Type=simple
Restart=always
ExecStart=/path/to/script

[Install]
WantedBy=multi-user.target

Тогда graceful restart будет работать

develf ()

Как-то так:

package main

import (
	"io/ioutil"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)

type App struct {
	confFile string
	whatever []byte
}

func (a *App) Load() error {
	p, err := ioutil.ReadFile(a.confFile)
	if err != nil {
		return err
	}
	a.whatever = p
	return nil
}

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write(a.whatever)
}

type Loader interface {
	Load() error
}

func Reload(l Loader, sig ...os.Signal) error {
	if err := l.Load(); err != nil {
		return err
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, sig...)
	go func() {
		for range c {
			if err := l.Load(); err != nil {
				panic(err)
			}
		}
	}()
}

func main() {
	app := &App{confFile: "config.txt"}
	Reload(app, syscall.SIGHUP) // reload configuration on SIGHUP
	http.Handle("/", app)
	http.ListenAndServe("localhost:8080", nil)
}

beastie ★★★★★ ()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от pru-mike

Разбавить по вкусу. Вот более атомарный вариант:

func (a *App) Load() error {
	p, err := ioutil.ReadFile(a.confFile)
	if err != nil {
		return err
	}
	*a = App{confFile: a.confFile, whatever: p}
	return nil
}

beastie ★★★★★ ()

best practices - это форкнуть процесс, а в текущем перестать делать accept, потому что есть такая штука как keepalive.

xpahos ★★★★★ ()
Ответ на: комментарий от beastie

go func() {
for range c {
if err := l.Load(); err != nil {
panic(err)
}
}
}()

Я правильно понимаю - здесь запускается отдельная нить^Wгорутина, из которой производится вызов метода объекта, исполняющегося на другой горутине, без всяких заморочек вроде синхронизации?

tailgunner ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.