Files
build-web-application-with-…/ru/03.4.md
2016-09-16 16:19:37 +03:00

6.8 KiB
Raw Blame History

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.

Ссылки