Разработка веб-серверов на Go
Разработка веб-сервера на языке программирования Go - достаточно лёгкая задача. В статье рассмотрим возможности для гибкой настройки работы сервера.
69К открытий71К показов
Стандартная библиотека языка Go включает в себя множество полезных и функциональных компонентов «из коробки», которые позволяют легко разрабатывать серверные приложения. В статье мы изучим, как написать веб-сервер на Go. Начнем с базового «Hello World!» и закончим приложением, выполняющим следующие функции:
- использует Let’s Encrypt для HTTPS;
- выполняет маршрутизацию запросов к API;
- реализует промежуточную обработку запросов (middleware);
- раздаёт статические файлы;
- корректно завершает свою работу.
Если вы хотите сразу увидеть готовый код, зайдите в репозиторий http-boilerplate на GitHub.
Hello World!
HTTP-сервер на Go пишется довольно быстро. Вот пример с одним обработчиком, возвращающим «Hello World!»:
Если вы запустите приложение и откроете в браузере страницу http://localhost, то увидите текст «Hello World!».
В дальнейшем мы используем этот код в качестве основы, но сначала нужно понять, как он работает.
Как работает net/http
В прошлом примере использовался пакет net/http, который служит в Go основным средством для разработки HTTP-клиентов и серверов. Чтобы разобраться в коде, следует кратко объяснить три важные концепции: http.Handler
, http.ServeMux
и http.Server
.
HTTP-обработчики
Обработчиком называется то, что принимает запрос и возвращает ответ. В Go обработчики реализуют интерфейс со следующей сигнатурой:
Наш первый пример использует вспомогательную функцию http.HandleFunc
, которая оборачивает другую функцию, принимающую http.ResponseWriter
и http.Request
, в ServeHTTP
.
Идея представить обработчики единым интерфейсом открывает много возможностей. Позже мы увидим, что промежуточная обработка запросов будет реализована обработчиком, чей метод ServeHTTP
выполняет некоторый код, а после вызывает метод ServeHTTP
другого обработчика.
Таким образом, обработчики формируют ответы на запросы. Но как понять, какой именно обработчик нужно использовать в данный момент?
Маршрутизация запросов
Для выбора обработчика запроса в Go используется HTTP-мультиплексор. В некоторых библиотеках он называется «muxer» или «router», но суть та же. Мультиплексор выбирает обработчик на основе анализа пути запроса.
Если вам нужна продвинутая поддержка маршрутизации, следует обратиться к сторонним библиотекам. Достойные альтернативы — gorilla/mux и go-chi/chi — позволяют простым образом реализовать промежуточную обработку, настроить wildcard-маршрутизацию и решить ряд других задач. Однако важнее то, что они совместимы со стандартными HTTP-обработчиками. Таким образом, сохранится простота кода и возможность его безболезненного изменения в будущем.
Будьте внимательны при работе со сложными веб-фреймворками. Обычно они используют нестандартные решения, что усложняет интеграцию со стандартными обработчиками. Как правило, комбинации стандартной библиотеки и легковесного маршрутизатора будет достаточно для разработки большинства приложений.
Обработка запросов
Наконец, нам необходимо что-то, способное слушать входящие соединения и перенаправлять каждый запрос соответствующему обработчику. Эту задачу можно возложить на http.Server
.
Как мы увидим далее, сервер отвечает за всё, что связано с обработкой соединений. В частности, сюда относится работа по протоколу TLS. В нашем примере при вызове http.ListenAndServe
используется стандартный HTTP-сервер.
Теперь перейдём к более сложным примерам.
Работа с Let’s Encrypt
Начальная версия нашего приложения работает по протоколу HTTP, но при возможности всегда рекомендуется использовать протокол HTTPS. К счастью, в Go это не проблема.
Если у вас уже есть сертификат и закрытый ключ, просто используйте ListenAndServeTLS
, указав корректные пути к файлам сертификата и ключа:
Но можно сделать лучше.
Let’s Encrypt — это удостоверяющий центр, выдающий бесплатные сертификаты с возможностью их автоматического обновления. Для использования этого сервиса в Go-приложениях доступен пакет autocert.
Настроить Let’s Encrypt проще всего используя вспомогательный метод autocert.NewListener
в связке с http.Serve
. Вспомогательный метод получает и обновляет TLS-сертификаты, в то время как HTTP-сервер занимается обработкой запросов:
Если требуется более тонкая настройка, используйте менеджер autocert.Manager
. Затем создайте сервер http.Server
(до сих пор мы использовали реализацию по умолчанию) и добавьте менеджер в конфигурацию сервера TLSConfig
:
Таким простым способом можно реализовать полную поддержку HTTPS с автоматическим обновлением сертификата.
Установка маршрутов
Маршрутизатор из стандартной библиотеки неплох, но недостаточно функционален. Как правило, в приложениях требуется более сложная логика маршрутизации. Например, настройка вложенных и wildcard-маршрутов или установка шаблонов и параметров путей.
В этом случае могут быть полезны пакеты gorilla/mux и go-chi/chi. Ниже описан пример настройки маршрутизации с помощью библиотеки chi.
Допустим, у нас есть файл api/v1/api.go, содержащий маршруты для нашего API:
В основном файле установим для маршрутов префикс api/v1
:
Возможность организации маршрутов и применение продвинутых правил маршрутизации упрощает структуризацию и сопровождение крупных приложений.
Реализация middleware
Промежуточная обработка (middleware) представляет собой обёртывание одного HTTP-обработчика другим. Это позволяет реализовать аутентификацию, журналирование, сжатие и другую функциональность.
Данный шаблон программирования несложно понять благодаря простоте интерфейса http.Handler
. Мы можем написать функцию, которая принимает один обработчик и оборачивает его в другой. Для примера рассмотрим обработчик с аутентификацией пользователей:
Некоторые сторонние маршрутизаторы наподобие chi предоставляют полезные и готовые функции для промежуточной обработки.
Раздача статических файлов
В стандартную библиотеку Go встроены возможности для раздачи статического контента, такого как изображения, файлы JavaScript и CSS. Они доступны через функцию http.FileServer
, которая возвращает обработчик, раздающий файлы из заданного каталога.
Простой пример с уже знакомым нам маршрутизатором:
Обратите внимание! Стандартный http.Dir
просто выводит содержимое каталога, если в нём нет файла index.html. Так может быть раскрыта конфиденциальная информация. Если такое поведение нежелательно, используйте пакет unindexed.
Корректное завершение работы
Начиная с версии 1.8 в Go есть возможность корректно завершать работу HTTP-сервера вызовом метода Shutdown()
. Мы запустим сервер в горутине и будем прослушивать канал для приёма сигнала прерывания (например комбинации CTRL+C). После получения сигнала выделим несколько секунд на корректное завершение работы сервера.
Заключение
Стандартная библиотека языка Go невероятно функциональна. В доказательство этого были рассмотрены её встроенные возможности и преимущества гибких интерфейсов для быстрой разработки надежных HTTP-серверов.
В качестве продолжения рекомендуем посмотреть статью на Cloudflare, чтобы узнать о предварительных шагах перед тем, как разворачивать HTTP-сервер на Go в Интернете.
Если вам интересны шаблоны, которые вы можете использовать в собственных приложениях, зайдите в проект http-boilerplate на GitHub. А чтобы увидеть описанные приемы программирования в реальных продуктах, посетите проект Gophish на GitHub.
69К открытий71К показов