Микросервис на Golang

в 13:24, , рубрики: Go, микросервисы

image Среди беспорядка найдите простоту; среди раздора найдите гармонию; в трудности найдите возможность...
(С) Альберт Эйнштейн

Статей о микросервисах, их достоинствах и недостатках в последнее время написано немало. Однако как-то редко кто пишет об имплементации микросервисной архитектуры, и прежде всего, именно об микросервисе, как о кирпичике, из которой и строится потом здание такого приложения. Я попытался восполнить этот пробел, и поделиться своим опытом в разработке микросервиса, вылившемся в конечном счёте в небольшую библиотеку под не оставляющим места для сомнений названием Microservice. Код написан на прекрасно подходящем для микросервисов, простом и удобном языке программирования Golang.

В последнее время мне довелось поработать над созданием микросервисов на языке Golang. Это был интересный опыт. Однако во многом получается (по крайней мере у меня), что каждый раз новый микросервис создаётся заново и с нуля. Конечно, можно использовать предыдущие наработки, но в таком подходе нет некоей системности, и мне захотелось сделать инструмент, который бы несколько упорядочил такую работу (искренне на это надеюсь). Не сомневаюсь, это не единственная идея в разработке микросервисов, но если вы ими интересуетесь, возможно, вам будет интересно прочитать данную статью.

Что такое микросервис, я думаю, во избежание холивара, тут не стоит говорить. Также и о достоинствах и недостатках промолчим. Отмечу лишь, что они есть, и именно благодаря наличию оных микросервисная архитектура востребована.

Архитектура микросервиса в моей библиотеке включает в себя хэндлер, тюнер (конфигуратор), очередь, пару демонстрационных middleware и хранилище для них. Работает всё очень просто: в приложении загружается конфигурация с учётом файла конфигурации, окружения и командной строки. Создаётся хранилище с middleware и под нужные роуты формируются соответствующие очереди. Далее запускается сервер и по запросу приложение отрабатывает нужные очереди.

Тюнер

Важным моментом для микросервиса является загрузка конфигурации. Я постарался сделать этот функционал максимально гибким. По умолчанию конфигурация загружается из указанного файла (`config.toml`). Адрес файла конфигурации можно изменить из командной строки, например так: вашсервис -confile config.toml Таким образом можно создать несколько разных конфигурационных файла и запускать микросервис с одной из конфигураций по выбору.

Поскольку в конфигурировании микросервиса задействован не только файл, но и переменные окружения и параметры командной строки, то уточню порядок и приоритетность конфигурирования. Самым низким приоритетом обладает конфигурация из файла. Если в операционной системе настроены переменные окружения, то они имеют более высокий приоритет, чем переменные из конфигурационного файла. Параметры командной строки имеют самый важный приоритет. Для изменения параметра в командной строке нужно указывать его название в виде названии раздела и названия параметра (с прописной буквы!). Вот пример изменения порта — вашсервис -Main/Port 85

Микросервис на Golang - 2Ещё немного о конфигурировании: помимо варианта с закидыванием конфигурации в заранее заготовленную структуру, вполне можно было бы использовать вариант импорта данных в обычный map, и потом спокойно использовать значения по ключу. У этого способа есть несомненное достоинство — не нужно добавляя данные в конфигурационный файл дублировать это в конфигурационной структуре. Т.е. всё проще и быстрее. Минусом же будет то, что ошибки неправильного указания ключа переносятся с этапа компиляции на этап выполнения, и кроме того, тут IDE уже не станет нам делать подсказки, что кстати, само по себе очень хорошо в плане защиты от опечаток и как следствие — ошибок.

Storage

На самом деле, хранилище middleware объектов Storage никак не обязательно для использования в микросервисе, и я не пытался реализовать что-нибудь из того, что часто связывают с DI. Тут скорее субъективное удобство создания и инициализации структур в одном месте. Кроме того, благодаря хранилищу IDE любезно подсказывает названия структур и их экспортируемых методов.

Middleware

Чтобы не засорять пространство хэндлеров, в микросервисе предусмотрено использование сервисного функционала в стиле middleware. К каждому такому сервису предъявляются минимальные требования: он должен принимать в качестве аргументов http.ResponseWriter, *http.Request и возвращать их. В дистрибутиве продемонстрирован пример с подключением метрики перед и после хэндлера для фиксации времени обработки запроса (duration).

Микросервис на Golang - 3В зависимости от того, какие задачи решаются микросервисом и в каком окружении он это делает, почти наверняка вам потребуются и другие сервисы, например валидация. Рассмотрите для них возможность подключения к хэндлеру в качестве middleware. И обратите внимание, что демонстрационные метрики и сессия вынесены в отдельный подкаталог, чтобы подчеркнуть дистанцию между микросервисом и используемыми в нём middleware.

Очередь

Эта сущность такая же простая, как и всё остальное. Она просто хранит список функций (хэндлера и мидлвар), и когда надо, запускает их на исполнение методом Run. При этом, если какой-то из методов возвратит nil вместо *http.Request, очередь прекращает выполнение (например это мог бы сделать валидатор, ну или контролёр доступа).

Handler

В дистрибутиве handler содержит в себе совсем мало кода и занимается в общем тем, что создаёт и возвращает очередь выполнения queue. Однако именно методы handler и являются теми хэндлерами, которые обработают поступивший запрос, всё остальное по сути обвес микросервиса. Если это так, то почему бы не сделать просто кучу автономных функций, которые и будет вызывать роутер? На это есть причина: благодаря объединению посредством структуры хэндлеры-методы теперь смогут иметь доступ (если потребуется) к полям структуры (общему контексту приложения). На самом деле очень удобно для каждого публичного метода (читай — обработчика) создать отдельный файл, например handle_hello_word.go, что впрочем не мешает организовать всё каким угодно другим образом.

Алгоритм работы с микросервисом

Создание нового Middleware

К примеру вы хотите добавить валидатор в качестве middleware. В этом случае сначала в файле конфигурации config.toml добавляете раздел и в нём нужные параметры. Например так:

[Validation]
Number	= "integer"

Теперь создаёте соответствующую структуру (например ConfValidation) в файле config.go и добавляете её там же в структуру Config. Далее в storage добавляете создание валидатора, и после этого его можно использовать при формировании очереди в main.go

Создание нового хэндлера

Для этого нужно просто создать новый публичный метод в handler, который на вход принимает http.ResponseWriter, *http.Request и возвращает их. Посмотрите созданный для демонстрации метод HelloWorld.

Perfomance

Микросервис на Golang - 4 Для общего понимания того, что скорость работы микросервиса при использовании предложенной архитектуры будет достаточно высокой, приведу результаты бенчмарка, полученные мной на моём компьютере IntelCore i3-2120:

  • BenchmarkMain-4 5000000 261 ns/op
  • BenchmarkMainParallel-4 10000000 137 ns/op

Зависимости

Любую из перечисленных библиотек можно заменить или дополнить, в данном случае они скорее призваны показать, в какую сторону развивать свой микросервис. Возможно, вам также полезно будет подключить логстеш и инфлюкс.

Заключение

Библиотека Microservice не претендует на лавры единственно верного решения, но при случае, надеюсь, сможет помочь вам сформировать архитектуру собственного микросервиса, став прототипом будущего приложения.

Ссылки

Автор: claygod

Источник

Поделиться

* - обязательные к заполнению поля