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

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

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

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

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

Тюнер


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

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

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

Middleware


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

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

Handler


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

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


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

Perfomance


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

  • BenchmarkMain-2 10000000 195 ns/op
  • BenchmarkMainParallel-2 10000000 107 ns/op


Зависимости



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

Заключение


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

Ссылки




UPD


С учётом комментариев немного изменил библиотечку, удалив хранилище и подправив метод организации мидлваре (без очередей), что кстати, даже немного ускорило работу приложения в бенче.
Используете ли вы микросервисную архитектуру?

Проголосовало 183 человека. Воздержалось 59 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Поделиться с друзьями
-->

Комментарии (25)


  1. antonksa
    29.12.2016 17:00
    +6

    Look at my horse, my horse is amazing! :D


  1. Terranz
    29.12.2016 17:19

    а как же авторизация?


    1. claygod
      29.12.2016 19:04

      Для авторизации как раз-таки стоит сделать отдельный микросервис


  1. dmrt
    29.12.2016 17:29
    -10

    Но черно-белой картинке глаза на вульгарные сиси похожи, особенно в профиль.


  1. mgremlin
    29.12.2016 19:28
    +2

    Это клево. Наверное. Если б хоть чего-нибудь понял — обязательно был бы более уверен!

    Автор! Нет бы хоть в общих чертах объяснить, для проходящих мимо. И примерчиков бы приложил пару штук! Не тут, так хоть в доках или на гитхабе.

    Иначе в массы не пойдет!
    ЗЫ. А вот роутер интересный, попробую на досуге, thanks.


    1. claygod
      30.12.2016 08:56
      -1

      mgremlin, дело в том, что описанная библиотека, она сразу же и пример. Т.е. если вы запустите её, то по адресу localhost увидите классический hello world.

      За хороший отзыв о роутере спасибо!


  1. afanasiy_nikitin
    30.12.2016 02:39
    +1

    Статья слишком сумбурна, чтобы применять ее в продакшене


    1. zelenin
      30.12.2016 03:09
      +1

      да и вообще «микросервис» здесь только в названии статьи. В самом коде нет ничего, что бы отличало его от простой заготовки веб-приложения.


      1. claygod
        30.12.2016 09:04
        -1

        Микросервис и должен быть прост, или Вы считаете иначе?

        Сам микросервис можно организовать как угодно, лишь бы давал правильные ответы, а я всего лишь предлагаю вариант. И если у Вас есть большой опыт создания микросервисов, то думаю, Вам не составит труда привести примеры и своё видение внутреннего строения микросервиса?


        1. zelenin
          30.12.2016 10:07
          +1

          Микросервис и должен быть прост, или Вы считаете иначе?
          изначально он должен обладать единичной зоной ответственности

          Вам не составит труда привести примеры и своё видение внутреннего строения микросервиса

          я говорю, что в вашем «микросервисе» нет ничего, чтобы отличало его от любой заготовки любого веб-приложения. И я против, чтобы все, что создается на go и смотрящим в веб автоматически называлось микросервисом. Строго говоря, микросервис может быть веб-приложением, но может и не быть. Так же справедливо и обратное: не каждое веб-приложение — микросервис. Соответственно и создали вы болванку того, что может быть микросервисом, а может и не быть — каких-то отличительных особенностей в коде нет.
          Добавьте сервис дискавери, сервисы по работе с апи/апи-шлюзом, балансировку, (g)rpc-клиент/сервер.
          Фреймворки для создания микросервиса на го уже существуют — можете ознакомиться с фичами.


          1. claygod
            30.12.2016 10:17
            -1

            Добавьте сервис дискавери, сервисы по работе с апи/апи-шлюзом, балансировку, (g)rpc-клиент/сервер.

            Вы описываете микросервисную архитектуру, а не микросервис.


            1. zelenin
              30.12.2016 10:19

              нет, я описываю фичи, которыми может и часто обладает единичный микросервис.


              1. claygod
                30.12.2016 10:28

                zelenin, если у вас всё это содержится в микросервисе, то он сам фактически содержит микросервисы. Обратите внимание, Вы сами пишете слово `сервисы` во множественном числе.


                1. zelenin
                  30.12.2016 10:43

                  я использую слово «сервисы» в другом контексте. Апи-клиент или клиент для сервис-дискавери являются сервисами приложения.


                  1. claygod
                    30.12.2016 10:52

                    Если сравнивать Ваше и моё видение микросервиса, то похоже, что Вы делаете микросервисы более «жирными». В принципе, если их ответственность при этом мала и неделима, это тоже вполне рабочий вариант. Мне кажется, я понимаю Вашу точку зрения. Если не затруднит, поглядите сделанный мной для фана ММОА.


                    1. zelenin
                      30.12.2016 11:02
                      +1

                      все не так. я лишь сказал, что вы сделали не заготовку под микросервис, а заготовку под веб-приложение. Оно может быть как приложением, отдающим данные по api, так и блогом. Но вы почему-то назвали это модным словом «микросервис», хотя для такой более узкой специализации у болванки нет никаких специализированных фич (не факт что они все и нужны в конкретном микросервисе).
                      Обсуждение исчерпало себя.


  1. umputun
    04.01.2017 10:10
    +3

    Удивительно непонятное описание продукта, просто исключительно странное. Даже мое понимание области в которой автор копает, не вносит ясности. Термины используется так, как я нигде не видел. Видимо очередь это не очередь а конвейер? И что за storage, для чего, куда и почему его основное достоинство это авто–дополнение в IDE? Хотя, похоже и storage это не то, что все под этим понимают, т.к. в нем автор предлагает хранить (?) middleware про которое я тоже уже не уверен, что это такое у автора и зачем его там хранить.

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

    Я не для придирок или прочих издевательств, но в попытке понять о чем это было вообще и как оно относится к микросервисам в частности.


    1. claygod
      04.01.2017 11:58

      Видимо очередь это не очередь а конвейер?

      Наверно для меня эти термины близки. Какую Вы принципиальную разницу вкладываете в эти два термина?

      middleware про которое я тоже уже не уверен, что это такое у автора и зачем его там хранить

      В моём понимании это промежуточное программное обеспечение. В библиотеке для примера это метрики, которые находятся «снаружи» микросервиса.

      И что за storage, для чего...

      Это простое хранилище объектов, оно только для удобства, чтобы инициализировать миддлваре объекты в одном месте.

      авто–дополнение в IDE

      Ну… это просто удобно, по крайней мере мне.

      но на иностранном языке, который немного похож на английский, но точно не он.

      Тут не в бровь, а в глаз, ибо учил я немецкий вперемежку с французским, так что транслятор, это наше всё… :(


      1. umputun
        04.01.2017 12:12

        в очередь я вкладываю понимание, что это queue куда кладут, в общем случае, данные. Кладут и достают последовательно. Конечно, очередь можно использовать мнoго для чего разного, например RPC, но насколько я понимаю, в статье не об этом речь. Конвейер, в моем понимании, это последовательность функциональных шагов для обработки неких данных.

        Для термина middleware в го мире есть довольно четкое соглашение, что это такое, и что люди под этим подразумевают, особенно в контексте http обработок. Например вот это https://github.com/pressly/chi/tree/master/middleware

        по поводу транслятора — по моему мнению лучше это поменять на оригинал до прогона через перводчик. То, что там читать трудно, а понять — невозможно.


        1. claygod
          04.01.2017 12:45

          umputun, ответил ниже (пардон, не туда запостил)


  1. claygod
    04.01.2017 12:19

    Конвейер, в моем понимании, это последовательность функциональных шагов для обработки неких данных.

    Спасибо, я подумаю над этим, может быть изменю названия, и тогда естественно, поправлю и в статье.

    Для термина middleware в го мире есть довольно четкое соглашение

    Если Вам не трудно, сформулируйте здесь (не только для меня), и тогда уж… то, что я называю миддлваре, как в принципе это назвали бы Вы?

    То, что там читать трудно, а понять — невозможно

    Скорей всего сокращу до простых предложений, которые и транслятор сможет осилить (надеюсь).


    1. umputun
      04.01.2017 21:09
      +2

      насколько я вижу, у тебя это тоже типа middlware, но с нестандартной сигнатурой func(http.ResponseWriter, *http.Request) (http.ResponseWriter, *http.Request). Если поменять на func(h http.Handler) http.Handler то станет как у всех, и самое главное — ты сможешь использовать все подобные, готовые middlware без всяких модификаций.

      Общее замечание: в принципе, идея создания фреймоврка такого рода мнe кажется делом сомнительным. Во первых, есть неплохие и проверенные боем библиотеки, которые это уже делают основнию часть, например chi. Во вторых — идея добавления туда и конфигурации и еще чего мне не кажется полезной. Да, я понимаю, что при создании сервиса хочется упростить начальные телодвижения, однако лично я пошел по другому пути и написал простой кодо-генератор, который делает рабочий скелет приложения с chi, набором middlware для него (у меня в каждом сервисе надо определенный набор, например JWT), логгером, main с флагами и env (github.com/jessevdk/go-flag) перехватчик сигналов и еще по мелочи. Кроме того, оно генерит не только исходный текст, но и все, что надо для сборки и деплоя (Dockerfile, CI yml, ansible yml)


      1. ninedraft
        05.01.2017 13:52

        А планируете ли вы выкладывать этот инструмент на гитхаб?


        1. umputun
          05.01.2017 18:34

          назвать это гордо «интрументом» я бы не решиился. Там весь код это 50-60 строк определяющие структуру параметров, заворачивающие «templates» со всем, что человек туда положил в bindata и делающее по всем файлам в нем filepath.Walk с ExecuteTemplate. Могу, конечно, выложить, просто мне казалось это тривиальным.


      1. claygod
        05.01.2017 15:54

        С учётом критики в комментах подправил библиотечку, в результате чего очереди и хранилище отвалились за ненадобностью, хотя сами мидлваре я всё же прикручиваю немного по другому.

        написал простой кодо-генератор, который делает рабочий скелет приложения

        Как-то не приходилось сталкиваться с кодогенерацией, насколько удобным Вам показался этот инструмент?