Go Guide 11 - Расскажите о принципе реализации HTTP-сервера Golang

Go

предисловие

Что касается принципа реализации HTTP-сервера Golang, в этой статье будут описаны следующие два момента, в надежде помочь всем!

  • 1. Как создать HTTP-сервер
  • 2. Принцип реализации HTTP-сервера

Как создать HTTP-сервер

СоздаватьHTTP服务器Шаг:

  • 1. Создать обработчик (собственно бизнес-логика)
  • 2. Создайте маршрутизатор и привяжите/зарегистрируйте его к процессору (чтобы соответствующая функция могла быть сопоставлена ​​​​по URL-адресу)
  • 3. Запустите HTTP-сервер и прослушивайте указанный порт.

Для реализации процессора их на самом деле всего два, один использоватьфункция обработчикаРеализация, вторая - создать структуру, иРеализовать ServeHTTPметод.

Что касается способа привязки между процессором и роутером, то он черезHandleFuncМетод напрямую привязан к процессору (например, метод записи 1);HandleПривязка направления изменения метода (например, метод записи 2).

Реализовано с помощью функций-обработчиков

// 使用处理器函数实现HTTP服务器 (写法一)
func index(w http.ResponseWriter, r *http.Request) {
	_, err := w.Write([]byte("Hello World"))
	if err != nil {
		fmt.Println("err", err)
	}
}

func httpServerByFunc() {
	http.HandleFunc("/", index)
	err := http.ListenAndServe(":5000", nil)
	if err != nil {
		panic(err)
	}
}


// 使用处理器函数实现HTTP服务器 (写法二)
func httpServerByFunc2()  {
	http.Handle("/", http.HandlerFunc(index))
	err := http.ListenAndServe(":5000", nil)
	if err != nil {
		panic(err)
	}
}

// 使用处理器函数实现HTTP服务器 (写法三)
func httpServerByFunc3() {
	// 匿名函数
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		_, err := w.Write([]byte("Hello World"))
		if err != nil {
			fmt.Println("err", err)
		}
	})
	err := http.ListenAndServe(":5000", nil)
	if err != nil {
		panic(err)
	}
}

Реализовано с использованием процессорной структуры

type MyHandler struct {

}

// 实现ServeHTTP方法
func (my *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	_, err := w.Write([]byte("Hello World"))
	if err != nil {
		fmt.Println("err", err)
	}
}

func httpServerByStruct()  {
	mux := http.NewServeMux()
	myHandler := MyHandler{}
	mux.Handle("/", &myHandler)

	// 注意这里传的是mux (mux也是一个Handler)
	err := http.ListenAndServe(":5000", mux)
	if err != nil {
		panic(err)
	}
}

Примечание. Не имеет значения, если вы сначала не понимаете приведенный выше пример, вы можете сначала прочитать следующий принцип, а затем вернуться, чтобы увидеть его, вы можете его понять.

Принцип реализации HTTP-сервера

Что касается принципа реализации HTTP-сервера, то, по сути, этоКак создать роутер, и как привязать роутер к процессорупроцесс. (лично я думаю, что это основная проблема)

Реализация HTTP-сервера Golang не очень сложна, но одним из наиболее болезненных моментов является то, что его имена функций (или сигнатуры функций) очень близки, поэтому вначале это будет очень запутанно, как правило, например,Handler,Handle,HandlerFuncтак же какHandleFunc, разница между этими четырьмя будет упомянута ниже, обратите внимание на исходный код и не читайте его неправильно.

Как создать маршрут

Есть два способа создать маршрут, первый — использовать маршрут по умолчанию, т. е.DefaultServeMux, второй - использоватьNewServeMuxметод создания маршрута, между этими двумя методами нет существенной разницы, но Golangсогласованное написаниеОн предназначен для определения структуры маршрутизации и одновременного предоставления методов маршрутизации по умолчанию и пользовательских методов маршрутизации.

Маршрутизатор HTTP определяется следующим образом:

// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that
// most closely matches the URL.
type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	es    []muxEntry // slice of entries sorted from longest to shortest.
	hosts bool       // whether any patterns contain hostnames
}

type muxEntry struct {
	h       Handler
	pattern string
}

Комментарий выше переводится как:ServeMuxЭто мультиплексор HTTP-запросов, который мы часто называем маршрутизатором. Он сопоставляет запрошенный URL-адрес с зарегистрированным URL-адресом. Если совпадение успешно, он будет передан обработчику для обработки.

Привязка маршрутизатора и процессора

Привязка между маршрутизатором и процессором в основном используетсяHandlerа такжеHandle.

Во-первых, с семантической точки зрения Handler представляет собой существительное, представляющеепроцессор; и Handle представляет собой глагол, представляющийиметь дело с. Соответственно есть HandlerFunc и HandleFunc.

Далее давайте посмотрим на разницу между Handler и Handle из исходного кода:

// A Handler responds to an HTTP request.
// Handler,即处理器,接收请求并响应
type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
// 注册Handler
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

Выше перечислены четыре сбивающих с толку функции или интерфейса, их функции следующие: 1.HandlerИнтерфейс: с технической точки зрения это действительно интерфейс и содержит метод ServeHTTP, но логически это обработчик, который используется дляПолучать HTTP-запросы и отвечать, и в то же время, пока существует структура, реализующая метод ServeHTTP, ее можно рассматривать как обработчик.

2.HandlerFuncТип: как видно из комментариев к исходному коду, это адаптер, функция которого заключается вПревратите обычную функцию в обработчик. Почему он разработан таким образом? Поскольку предыдущий способ создания процессора слишком громоздкий и недостаточно лаконичный, необходимо создать структуру и реализовать метод ServeHTTP.

Вот почему наши общие процессоры такие:

func index(w http.ResponseWriter, r *http.Request) {
}

Так как же HandlerFunc преобразует обычную функцию в обработчик?

Ответ находится в исходном коде ниже.Хотя HandlerFunc является типом, здесь он выглядит как структура и реализует метод ServeHTTP.Это означает, что HandlerFunc также является обработчиком., поэтому в качестве обработчика может выступать обычная функция.

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
// 调用我们实际的handler函数
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

3.Handleа такжеHandleFunc:Функции этих двух методов одинаковые,оба прописывают процессор в роутер.Основное отличие между ними в том,что HandleFunc инкапсулирует слой поверх Handle,вот и всё(в этом можно убедиться просмотрев исходный код)

Мы можем сделать краткое изложение вышеуказанных трех пунктов:ServeHTTPа такжеHandlerFuncЭффект заключается в созданииHandler,а такжеHandleа такжеHandleFuncОн предназначен для привязки сгенерированного обработчика к маршрутизатору, или, можно сказать,регистр.

Окончательный вывод таков:
1. Реализация HTTP-сервера включает маршрутизаторы, процессоры и сопоставление между ними.
2. Существует два типа маршрутизаторов: один использует маршрут по умолчанию (DefaultServeMux), другой использует метод NewServeMux для создания маршрута. Между ними нет существенной разницы.
3.Handler представляет процессор, его рольПолучать HTTP-запросы и отвечать, где мы пишем бизнес-логику, приведенный выше примерindex、loginвсе процессоры
4. С точки зрения исходного кода Handler — это интерфейс, включающийServeHTTPметод, что означает, что пока существует структура, реализованнаяServeHTTP, он может выступать в роли процессора.
5. Из-за实现ServeHTTP才能成为一个处理器Этот метод является избыточным и недостаточно кратким, поэтомуHandlerFuncТип, его рольПревратите обычную функцию в обработчик
6. Стоит отметить, что сам ServeMux также реализует метод ServeHTTP, то есть он тоже процессор, но его роль толькопересылать запросы другим процессорам.

Наконец, что касается принципа реализации HTTP-сервера, фактических знаний здесь гораздо больше, чем указано выше.Эта статья посвящена только созданию маршрутизаторов и процессоров и их связыванию.Я надеюсь, что это может помочь всем!

Пополнить

ServeHTTP для ServeMux

Это просто для того, чтобы доказать, что ServeMux также реализует метод ServeHTTP.

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
	if r.RequestURI == "*" {
		if r.ProtoAtLeast(1, 1) {
			w.Header().Set("Connection", "close")
		}
		w.WriteHeader(StatusBadRequest)
		return
	}
	h, _ := mux.Handler(r)
	h.ServeHTTP(w, r)
}

Проблема с HTTP-маршрутизацией Закрытие

Примеры следующие:

func routerClosure()  {
	// 当方法名/URL 是动态获取的时候
	methods := []string{"index", "login", "logout"}

	for _, m := range methods {
		http.HandleFunc("/" + m, func(w http.ResponseWriter, r *http.Request) {
			fmt.Println("method", m)
			w.Write([]byte("method name is : " + m))
		})
	}

	err := http.ListenAndServe(":5000", nil)
	if err != nil {
		panic(err)
	}
}

В ходе тестирования вы обнаружите, что независимо от индекса или входа в систему он в конечном итоге будет перенаправлен на обработчик выхода из системы, что вызвано замыканиями.

$ curl http://127.0.0.1:5000/index
method name is : logout% 

$ curl http://127.0.0.1:5000/login
method name is : logout% 

Решение выглядит следующим образом:

func routerClosure2()  {
	// 当方法名/URL 是动态获取的时候
	methods := []string{"index", "login", "logout"}

	for _, m := range methods {
		newM := m
		http.HandleFunc("/" + newM, func(w http.ResponseWriter, r *http.Request) {
			fmt.Println("method", newM)
			w.Write([]byte("method name is : " + newM))
		})
	}

	err := http.ListenAndServe(":5000", nil)
	if err != nil {
		panic(err)
	}
}

Ссылаться на

Шаблон использования пакета net/http языка Go