From 91e01701b59db2d158a53db8a39df9b6800a6cbb Mon Sep 17 00:00:00 2001 From: kasmanavt Date: Fri, 16 Sep 2016 16:19:37 +0300 Subject: [PATCH] add ru translation for 03.4 --- ru/03.4.md | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 ru/03.4.md diff --git a/ru/03.4.md b/ru/03.4.md new file mode 100644 index 00000000..a346c6d7 --- /dev/null +++ b/ru/03.4.md @@ -0,0 +1,136 @@ +# 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) +