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

137 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)