6.8 KiB
3.4 Внутренний мир пакета http
В предыдущих разделах мы узнали о том, как работает Веб и немного затронули работу с пакетом http. В данном разделе мы изучим две основные функции этого пакета: Conn и ServeMux.
Использование горутин в функции Conn
В отличи от обычных HTTP серверов, Go использует гоурутины при каждом обращении создаваемые функцией Conn. За счет эго обеспечивается высокая производительность и параллельная обработка.
Go использует следующий код для ожидания новых подключений от клиента:
c, err := srv.newConn(rw) if err != nil { continue } go c.serve()
Как вы видите, горутины создаются для каждого подключения. При этом в горутину передает обработчик, способный читать данные из запросов.
Настройка ServeMux
В предыдущем разделе, когда рассматривали метод conn.server, мы использовали роутер по умолчанию.
Структура роутера по умолчанию:
type ServeMux struct { mu sync.RWMutex // because of concurrency, we have to use mutex here m map[string]muxEntry // router rules, every string mapping to a handler }
Структура muxEntry:
type muxEntry struct { explicit bool // exact match or not h Handler }
Интерфейс Handler:
type Handler interface { ServeHTTP(ResponseWriter, *Request) // routing implementer }
Handler - это интерфейс, однако, функция sayhelloName не реализует этот интерфейс. Почему, в таком случае, мы смогли использовать ее в качестве обработчика? Потому, что в пакете http существует другой тип HandlerFunc. В нашем нашем сервере из раздела 3.2 при вызове HandlerFunc происходит автоматическое приведение нашей функции sayhelloName к интерфейсу Handler. Это равносильно вызову HandlerFunc(f), при этом f будет принудительно привдена к типу HandlerFunc.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP вызывает f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
Как маршрутизатор вызывает обработчики после установки правил?
Маршрутизатор вызывает mux.handler.ServeHTTP(w, r) при получении запросов. Другими словами, он вызывает ServeHTTP интерфейсы обработчиков.
Давайте посмотрим как работает mux.handler.
func (mux *ServeMux) handler(r *Request) Handler { mux.mu.RLock() defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones h := mux.match(r.Host + r.URL.Path) if h == nil { h = mux.match(r.URL.Path) } if h == nil { h = NotFoundHandler() } return h }
Маршрутизатор использует URL-адрес в качестве ключа для поиска соответствующего обработчика, который сохранен в карте и вызовов handler.ServeHTTP для выполнения функций обработки данных.
Теперь вы должны понимать принципы работы роутера. Фактически, Go поддерживает настраиваемые роутеры. Второй аргумент функции ListenAndServe необходим для конфигурации настраиваемого роутера с типом Handler. Таким образом любой роутер реализует интерфейс Handler.
Следующий пример покажет, как реализовать простой роутер.
package main
import ( "fmt" "net/http" )
type MyMux struct { }
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { sayhelloName(w, r) return } http.NotFound(w, r) return }
func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello myroute!") }
func main() { mux := &MyMux{} http.ListenAndServe(":9090", mux) }
Исполнение кода по шагам
Давайте посмотрим на поток выполнения.
- Вызывается
http.HandleFunc.
- Вызывается HandleFunc из DefaultServeMux.
- Вызывается Handle из DefaultServeMux.
- Добавляются правила маршрутизации в карту map[string]muxEntry из DefaultServeMux.
- Вызывается
http.ListenAndServe(":9090", nil).
- Создается экземпляр Server.
- Вызывается ListenAndServe для Server.
- Вызывается net.Listen("tcp", addr) для прослушки порта.
- Запускается бесконечный цикл, в теле которого происходит прием запросов.
- Создается экземпляр Conn и запускаются горутины для каждого запроса:
go c.serve(). - Читаются данные запроса:
w, err := c.readRequest(). - Проверяется существует ли обработчик и если обработчика нет используется DefaultServeMux.
- Вызывается ServeHTTP для обработчика.
- Исполняется код в DefaultServeMux в нашем случае.
- Выбирается обработчик, соответсвующий URL, и исполняется код обработчика:
mux.handler.ServeHTTP(w, r) - Как выбирается обработчик: A. Проверяются правила маршрутизации по данному URL. B. Вызывается ServeHTTP в данном обработчике, если он есть. C. В противном случае вызывается ServeHTTP для NotFoundHandler.
Ссылки
- Содержание
- Предыдущий раздел: Как Go работает с веб
- Следующий раздел: Итоги раздела