Как мы пишем микросервисы и почему не делаем этого быстро


    Истории по распиливанию монолита часто похожи одна на другую. Был у команды здоровенный неповоротливый монолит, решили его распилить на россыпь правильных и шустреньких микросервисов, все стало круто. Отличаются истории лишь степенью ужаса “до”, радости “после” и рядом вторичных характеристик.


    У нас в RBK.money тоже микросервисы. Но пришли мы к ним немного не так, как большинство. У нас все было даже хуже монолита — у нас на старте просто все было хреново.


    Под катом о том, как мы, собственно, и строили микросервисы, почему OpenSource — это не только здорово в принципе, но еще и работает как мотивационная составляющая писать хороший код.


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


    Быстро vs хорошо


    В идеальном мире всегда хочется писать код быстро и писать его хорошо. Ну это как “Лучше быть богатым и здоровым, чем бедным и больным”. Поэтому микросервисы стали отличным выходом из ситуации. Процесс написания кода был построен от бизнес-задач. Допустим, бизнесу нужна функциональность, которая будет учитывать средства на счетах контрагентов при платежах. Эта функциональность превращается в микросервис под кодовым именем Accounter, который и занимается учетом средств. С остальными микросервисами такая же история.


    Главным тут было сделать так, чтобы каждая бизнес-функциональность была настолько конкретизирована, чтобы её мог писать один человек. Это сильно зависит и от самих задач, поступающих в работу, и от того, как техдир или проджект транслирует это команде. У нас получилось так сделать, это даёт сразу пару хороших весомых плюсов.


    Во-первых, это обеспечивает большую параллелизацию разработки. У нас на старте было человек 10, и мы умудрялись писать одновременно большое количество кода (и писать хорошо). Во-вторых, это дарит вам возможность полноценной ротации. А вот это уже немного важнее, чем кажется на первый взгляд.


    Очень часто человек начинает говнокодить не потому, что он за этим и устраивался к вам работать, а потому, что у него банально глаз замыливается и скучно становится. Если один человек постоянно сидит на одном и том же микросервисе, он может начать генерить говнокод. И это не столько вопрос профессионализма, сколько вопрос времени. Месяцев через 7-8 человек устанет поддерживать один и тот же микросервис, будет смотреть по сторонам — а там жизнь, после зимы пришла весна, у коллег какая-то движуха, опять новый айфон вышел, а ты все сидишь на одном и том же микросервисе. Вот так рождается монолит с единой точкой отказа в виде этого уставшего человека с мешками под глазами.



    Или вообще человек начинает думать, что тут все только на нем и держится. Будет стараться сделать себя незаменимым, окружив свою работу кучей “тайных знаний” и странных процедур. У меня здесь в начале пути были ситуации, когда легаси было настолько диким, что без этих самых знаний разобраться было невозможно. Например, надо было запустить один сервис. Как себе обычно представляешь такое:


    1. Запустить сервис.

    Как было:


    1. Зайти в реестр винды.
    2. Найти там определенный ключ.
    3. Поменять его на 1.
    4. Запустить сервис.
    5. Сбросить значение ключа на 0.

    Это классический пример сложности ради сложности и “Без меня тут ничего не работает”. На самом деле, без подобного всё работает. Только быстрее и лучше. Избавиться от этого можно ротацией — в идеале когда человек пишет один микросервис примерно пару спринтов, а потом уходит делать другую задачу. Этим же мы поддерживаем и постоянный обмен знаниями в команде.


    Код от протокола



    Если взять любое ТЗ от бизнеса, хорошенько перевести на человеческий, отряхнуть от шелухи и выпарить — получится протокол, язык, по которому и будет общаться машина. То есть мы берем бизнес-задачу, понимаем для себя, что именно и как мы будем делать, и превращаем это в спецификацию на thrift или swagger (микросервисы внутри общаются по thrift). Первый шаг — всё подробно описать: что будет делать микросервис, какие типы данных принимать, чем отвечать, какие будут структуры и прочее. Этот протокол проходит первое ревью у тех, кто точно представляет себе, как все работает (де-факто — архитекторы). Срабатывает как фильтр грубой очистки, через который какое-то откровенное фуфло не пройдет даже на уровне концепции.


    Как только появляется протокол, можно садиться писать код. И если протокол ревьюится вполне себе универсальными людьми, то сам код — в команде конкретных людей. Мы пишем на трёх языках — JS, Java, Erlang. Главное — никого не торопить ни с ревью, ни с написанием кода. Да, бизнесу всегда и везде надо быстро и круто. Но я как техдир редко тороплю ребят, потому что понимаю, что они хотят сделать хорошо. В итоге получается ситуация, что я часто бываю взбодрен бизнес-заказчиками за сроки. Зато практически не приходится краснеть за качество.


    Мы поспешили лишь однажды, когда наложился джекпот — суперзаказчик и крайне срочные дедлайны, как раз создавали наш Wallet. Тогда да, мы чууууток спустили рукава и сделали все быстрее, чем планировали (и хуже, чем хотели, да). В идеале все задумывалось как кучка аккуратных микросервисов. Получился такой себе кусочек монолита. Плюсы ситуации в том, что мы еще разок для себя поняли, что спешить не надо. А сам сервис уже потихоньку растаскиваем на отдельные микросервисы, как и хотели.



    Микросервисов в RBK Money штук 50, их пишут около 20 человек. Внутри везде thrift, для разрабов это довольно сложный протокол, дебажить сложно, документацию писать тоже сложно. И если бы я выпустил thrift наружу в чистом виде, меня бы стали называть нехорошими словами. Поэтому мы не стали ничего придумывать — наружу у нас бодро торчит рестовый JSON, простой и понятный, плюс OpenAPI. Чтобы иметь возможность принимать эти запросы снаружи, их надо валидировать, авторизовывать, а потом другими микросервисами уже запускать внутрь платформы. И всё это дело мы тоже написали в качестве самостоятельного микросервиса, который:


    • принимает снаружи swag;
    • валидирует схему;
    • авторизует пользователя;
    • превращает всё это в thrift-запрос;
    • ну и логи пишет, конечно.

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



    Но есть мнение, что если один человек сидит и тщательно пилит конкретный микросервис довольно долго, то он точно делает это хорошо. Раз уж мы заговорили об этом — напишите, пожалуйста, в комментариях, какой подход вам ближе. А главное — почему.

    RBK.money
    RBK.money – современная платежная платформа

    Комментарии 23

      0

      Полностью согласен, ротация обязательно должна быть — при этом в целом нет никакой разницы, какой архитектурой вы пользуетесь. Если вкратце:


      1) Увеличивается bus factor;
      2) Разные команды тащат свои лучшие практики;
      3) Часто каждый следующий участник, который не понимает, как работает написанный до него код, разбирается и делает его проще;
      4) Добавляется документация, становятся проще тесты, деплой и запуск;
      5) Все разработчики получают знания о каких-то инструментах, которые применяют другие команды.


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


      При этом, правда, у нас остаётся понятие code owners — это те ребята, которые готовы подхватить сервис в случае срочных проблем, и следят, чтобы другая команда не сделала код хуже.

        0

        Тут согласен, в любой архитектуре такой подход важен. У нас если 1 микросервис ~ 1 разработчик не означает, что он пилится в одно лицо. Код ревью обязателен в любом случае, code owners даже более расширен — там девопсы и инфосеки смотрят обязательно.

        0
        Всегда работала поговорка «семь раз отмерь, один раз отрежь». Конечно если учесть, что при проектировании учитываются входные и выходные данные, то тут сложно ошибиться. Это как у инженеров, есть чертеж, указаны размеры детали, берешь и пилишь и по другому не сделаешь.
          +2

          А если требования поменяются, или так у взрослых не бывает?

          0

          Как поддерживаете консистентность данных если бизнес-операция охватывает несколько микросервисов?

            0

            Есть один микросервис, который отвечает за хранение состояний, а также обеспечивает гарантии — атомарность, тотал ордеринг, изоляцию.

              0

              Спасибо за ответ. Может быть подумаете о статье на эту тему? Было бы интересно почитать.

                0

                Статьи, книжки, теоремы и саги уже написаны :) осталось их нагуглить

                  0

                  Да, сам использую saga orchestration. Но в русскоязычных докладах на тему "Как мы внедрили микросервисы" — это почти всегда не так и даже интересно как и зачем люди пытаются изобрести ACID в распределенной системе и почему уверены, что их решение действительно работает верно.

                  +1

                  ок, напишу

                  +1
                  Есть один микросервис, который отвечает за хранение состояний

                  Разве это не противоречит высказыванию


                  отсутствие единой точки отказа
                    +1

                    Ага, это вы очень верно подметили. Он у нас долгое время был синглтоном и его отказ означал отказ всего сервиса. Сейчас это распределенный сервис, синхронизация по рафту, отказ одной ноды означает временную недоступность конечных автоматов, но и они в итоге переползут на активную ноду.

                    0
                    Один сервис, отвечающий за хранение состояний — не бывает.
                    Простейший случай — платеж на фронте (да, он должен где-то сохраниться на фронте), и дальше тот же платеж в бэке (тут вся жесть связанная с бухгалтерией).
                    И начинаем потихоньку накидывать — различные посредники/агрегаторы платежей, различные каналы (кто-то всегда ходит в фронт/бэк, кто-то хочет получать пуши), различные сопутствующие артефакты с которыми тоже нужна консистентность (sms, взаиморасчеты, активация услуг и т.д.)
                    В итоге получаем с десяток мест, где хранится состояние.
                      +1

                      Ну даже не знаю что вам ответить. Мы не храним платежи на фронте, да и не накидываем различные сущности. Вы нас, наверное с каким-то банком или обычной платежкой путаете, а мы — не такие.

                        0
                        Вам повезло. Всем прочим приходится воевать за консистентность.
                          +1

                          Вы всегда можете поставить себе процессинг от РБК и стать как мы. Правда, это только про платежи.

                  +3
                  А мне не нравится идея ротации, лучше знать свою программу хорошо, чем всего по чуть-чуть и ничего в деталях. Может, это у нас сложный монолит, в котором человеку нужно полгода, чтобы въехать в общих чертах, как что работает, и несколько лет, чтобы разобраться во всех деталях.

                  Я вот не один год на проекте и чувствую, что это даёт ощутимое преимущество. Когда проблема на лайве, обычно сразу понимаю, в какой файл смотреть и что может быть причиной. Если кто-то поменял код в одном месте, я пишу в ревью, что нужно поменять во втором и третьем, чтобы система работала одинаково (или просто чтобы ничего не сломалось).
                    +1
                    Если кто-то поменял код в одном месте, я пишу в ревью, что нужно поменять во втором и третьем, чтобы система работала одинаково
                    В статье про уникальную экспертизу особенностей работы кода написано. Не знаю подробностей, но вашу ситуацию можно назвать high coupling, я думаю.
                    +2
                    Мы поспешили лишь однажды, когда наложился джекпот — суперзаказчик и крайне срочные дедлайны, как раз создавали наш Wallet. Тогда да, мы чууууток спустили рукава и сделали все быстрее, чем планировали (и хуже, чем хотели, да). В идеале все задумывалось как кучка аккуратных микросервисов. Получился такой себе кусочек монолита. Плюсы ситуации в том, что мы еще разок для себя поняли, что спешить не надо. А сам сервис уже потихоньку растаскиваем на отдельные микросервисы, как и хотели.

                    Помоему очень прагматичный и гибкий (Аджайл ;) ) понимание у вашей организации.
                    Вы ответили на запрос клиета когда это пришлось, взяв технический долг, но у вас есть вобзможность или свобода оплатить его!

                      +1

                      Ваши слова как бальзам на душу, спасибо)

                        0

                        Но когда заказчик понимает, что получил быстро и криво, может и репутация пострадать...

                          0

                          Кто сказал что криво?

                        0

                        По мне так, если нет ротации, то должны работать очень толковые люди. За свою карьеру встречал таких мало-мало. Поэтому полностью поддерживаю идею ротации.

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

                        Самое читаемое