some fix for 03.4

This commit is contained in:
kasmanavt
2016-09-16 17:01:53 +03:00
parent 91e01701b5
commit 179ad2b05d

View File

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