# 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`. 1. Вызывается HandleFunc из DefaultServeMux. 2. Вызывается Handle из DefaultServeMux. 3. Добавляются правила маршрутизации в карту map[string]muxEntry из DefaultServeMux. - Вызывается `http.ListenAndServe(":9090", nil)`. 1. Создается экземпляр Server. 2. Вызывается ListenAndServe для Server. 3. Вызывается net.Listen("tcp", addr) для прослушки порта. 4. Запускается бесконечный цикл, в теле которого происходит прием запросов. 5. Создается экземпляр Conn и запускаются горутины для каждого запроса: `go c.serve()`. 6. Читаются данные запроса: `w, err := c.readRequest()`. 7. Проверяется существует ли обработчик и если обработчика нет используется DefaultServeMux. 8. Вызывается ServeHTTP для обработчика. 9. Исполняется код в DefaultServeMux в нашем случае. 10. Выбирается обработчик, соответсвующий URL, и исполняется код обработчика: `mux.handler.ServeHTTP(w, r)` 11. Как выбирается обработчик: A. Проверяются правила маршрутизации по данному URL. B. Вызывается ServeHTTP в данном обработчике, если он есть. C. В противном случае вызывается ServeHTTP для NotFoundHandler. ## Ссылки - [Содержание](preface.md) - Предыдущий раздел: [Как Go работает с веб](03.3.md) - Следующий раздел: [Итоги раздела](03.5.md)