Остановка HTTP-сервера в Go
Сервер останавливают через контекст, не обрывая уже идущие ответы. Вместо бесконечного ожидания в главной функции держат значение типа сервера, запускают прослушивание в горутине и вызывают команду остановки с контекстом.
Контекст с коротким сроком или ручной
отменой передают в остановку: сервер
перестает принимать новые соединения
и дожидается завершения текущих. Ошибка
ErrServerClosed после остановки
считается нормальным исходом, а не сбоем.
Запустим сервер и остановим его через секунду отдельной веткой:
package main
import (
"context"
"fmt"
"log"
"net/http"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /ping", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "pong")
})
srv := &http.Server{Addr: "127.0.0.1:8080", Handler: mux}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()
go func() {
time.Sleep(time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal(err)
}
}()
select {}
}
Через секунду прослушивание завершится,
а главная функция остается в ожидании
для учебного примера. В рабочей программе
вместо пустого select часто ждут
сигнал или другую отмену контекста.
Тот же прием с отменой из контекста,
созданного в main:
package main
import (
"context"
"fmt"
"log"
"net/http"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mux := http.NewServeMux()
mux.HandleFunc("GET /ok", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "ok")
})
srv := &http.Server{Addr: "127.0.0.1:8080", Handler: mux}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()
go func() {
time.Sleep(500 * time.Millisecond)
cancel()
shCtx, shCancel := context.WithTimeout(context.Background(), time.Second)
defer shCancel()
_ = srv.Shutdown(shCtx)
}()
<-ctx.Done()
fmt.Println("stopped")
}
Дан объект сервера с адресом
127.0.0.1:8080 и мультиплексором
с маршрутом /ping. Напишите
полный main.go: запустите
прослушивание в горутине и остановите
сервер через контекст с сроком одна секунда
после короткой паузы.
Дан следующий код:
err := srv.ListenAndServe()
if err != nil {
log.Fatal(err)
}
Скажите, какую проверку ошибки добавить после штатной остановки сервера.
Дан запущенный сервер без вызова остановки.
Скажите, чем завершится программа при
закрытии терминала и прерывании процесса
по сравнению с Shutdown.