Три способа открыть http-сервис в Golang

задняя часть Go сервер HTTP

предисловие

Говорят, что стандартная библиотека go практична, а дизайн API прост. На этот раз я буду использовать пакет net/http из стандартной библиотеки go для реализации краткого веб-сервера http, включая три версии.

Самая простая версия v1

Используйте http.HandleFunc(partn, function(http.ResponseWriter,
*http.Request){})
HandleFunc принимает два параметра: первый — это адрес маршрутизации, а второй — метод обработки.

//v1
func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	     w.Write([]byte("httpserver v1"))
       })
       http.HandleFunc("/bye", sayBye)
       log.Println("Starting v1 server ...")
       log.Fatal(http.ListenAndServe(":1210", nil))
 }

func sayBye(w http.ResponseWriter, r *http.Request) {
      w.Write([]byte("bye bye ,this is v1 httpServer"))
}

Пользовательский обработчик v2

Глядя на исходный код стандартной библиотеки, версия v1 фактически вызывает метод handle, а входящий HandlerFunc реализует метод ServeHTTP обработчика, который на самом деле ServeHTTP выполняет обработку http-запроса.

HandleFunc调用.png

HandleFunc实现Handler.png

Handler接口定义.png

Исходя из этого, мы можем настроить наш собственный обработчик.Код версии v2 выглядит следующим образом:

// v2
func main() {
   mux := http.NewServeMux()
   mux.Handle("/", &myHandler{})
   mux.HandleFunc("/bye", sayBye)

   log.Println("Starting v2 httpserver")
   log.Fatal(http.ListenAndServe(":1210", mux))
}
type myHandler struct{}

func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
      w.Write([]byte("this is version 2"))
}
func sayBye(w http.ResponseWriter, r *http.Request) {
     w.Write([]byte("bye bye ,this is v2 httpServer"))
}

Кастомная конфигурация сервера v3

Мы уже вонзали нож в Handler, давайте посмотрим, какие секреты скрыты в http.ListenAndServe().

ListenAndServe.png
Оказывается, здесь можно настроить конфигурацию http сервера, все в структуре Сервера.Этот объект может настроить порт адреса прослушивания, настроить таймаут чтения и записи, настроить обработчик, настроить максимальное количество байт в заголовке запроса ..., все они немного изменены v2 Программа получает версию v3:

// v3
func main() {
    mux := http.NewServeMux()
    mux.Handle("/", &myHandler{})
    mux.HandleFunc("/bye", sayBye)

    server := &http.Server{
	    Addr:         ":1210",
	    WriteTimeout: time.Second * 3,            //设置3秒的写超时
	    Handler:      mux,
    }
    log.Println("Starting v3 httpserver")
    log.Fatal(server.ListenAndServe())
}

type myHandler struct{}

func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("this is version 3"))
}

func sayBye(w http.ResponseWriter, r *http.Request) {
       // 睡眠4秒  上面配置了3秒写超时,所以访问 “/bye“路由会出现没有响应的现象
    time.Sleep(4 * time.Second)              
    w.Write([]byte("bye bye ,this is v3 httpServer"))
}
Развернуть его (как плавно закрыть http сервис)

В версии go1.8 была добавлена ​​новая функция, использующая Shutdown(ctx context.Context) для корректного закрытия службы http.
В документации описаны:
Завершение работы закроет активные соединения без прерывания работы, а затем корректно остановит службы. Процесс обработки выглядит следующим образом:

  1. Сначала закройте все слушатели;
  2. затем закройте все неиспользуемые соединения;
  3. Затем бесконечно ждать, пока соединение будет обработано, станет бездействующим и закроется;
  4. Если предоставляется контекст с тайм-аутом, ошибка тайм-аута контекста будет возвращена до закрытия службы;

Используйте эту функцию, чтобы преобразовать версию программы v3 для реализации запроса на закрытие http

 // 主动关闭服务器
var server *http.Server
func main() {
    
 // 一个通知退出的chan
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)

mux := http.NewServeMux()
mux.Handle("/", &myHandler{})
mux.HandleFunc("/bye", sayBye)

server = &http.Server{
	Addr:         ":1210",
	WriteTimeout: time.Second * 4,
	Handler:      mux,
}

go func() {
    // 接收退出信号
	<-quit
	if err := server.Close(); err != nil {
		log.Fatal("Close server:", err)
	}
}()

log.Println("Starting v3 httpserver")
err := server.ListenAndServe()
if err != nil {
    // 正常退出
	if err == http.ErrServerClosed {
		log.Fatal("Server closed under request")
	} else {
		log.Fatal("Server closed unexpected", err)
	}
}
  log.Fatal("Server exited")

}

type myHandler struct{}

func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("this is version 3"))
}

 // 关闭http
func sayBye(w http.ResponseWriter, r *http.Request) {
      w.Write([]byte("bye bye ,shutdown the server"))     // 没有输出
      err := server.Shutdown(nil)
      if err != nil {
	    log.([]byte("shutdown the server err"))
      }
 }

Если вы попытаетесь получить доступ к http://localhost:1210/bye, вы получите следующие быстрые результаты в консоли, и служба http будет плавно закрыта:

成功平滑关闭.png