Привет, Хабр!

Напоминаем, что все желающие по-прежнему могут приобрести отличную книгу Сэма Ньюмена "Создание микросервисов". Поскольку наши ожидания эта тема более чем оправдала, мы продолжаем искать связанную с ней литературу и не так давно обратили внимание на книгу о программировании микросервисов на языке Go



Интересную статью с обоснованием этого подхода мы нашли в блоге Agile Maverick, и ее перевод размещаем под катом.

Приятного чтения!



Сейчас все говорят о микросервисах и DevOps. Поставьте эти словечки себе в профиль – и вас сразу начнут осаждать рекрутеры. Я побывал в Мюнхене на нескольких интересных митапах по микросервисам, и меня наиболее удивило, что эта тема пользуется наибольшим интересом в сообществах Java и Scala. Удивило потому, что Java и Scala – очень насыщенные языки, в которых есть из чего выбирать.

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

Я понимаю микросервисную архитектуру в более целостном ключе. Я бы сказал, что и вся экосистема должна быть проще и компактнее. Реализация – лишь одна сторона медали. Другая сторона – это время исполнения и сопутствующие фреймворки. Здесь мы подходим к теме DevOps – философии, стремящейся увязать друг с другом две эти стороны.

Виртуальная машина Java оптимизирована для работы с долгоиграющими приложениями, в ней действует одна из наиболее выверенных и затейливых систем сборки мусора. Используется в боевых условиях уже более 10 лет. Тем не менее, когда мне доводится видеть современные высокодоступные архитектуры – сразу напрашивается вопрос: а нужны ли долгоиграющие приложения для реализации абсолютного большинства существующих сервисов?

Приведу пример. Я участвовал в разработке приложения для кодировки видео, и это приложение как назло должно было работать круглосуточно с минимальными задержками. Мы думали, остановиться ли на стабильном языке программирования вроде Java или написать приложение на Go, где использовались бы имеющиеся библиотеки на C для кодирования и декодирования, однако такой проект мог обернуться утечками в памяти. Наконец, мы решили разделить приложение на различные процессы; статический бэкенд почти не изменился, поскольку передавал информацию по практически не изменившемуся протоколу, а еще у нас была функционально богатая клиентская часть, где существовал риск утечек. Обе части использовали разделяемую память. Оказалось, что вариант хороший. Поскольку Go стартует быстро, мы перезапускали клиентскую часть раз в десять секунд. Оказалось, что проблема – не в утечках памяти, а в оперативных обновлениях.

За много лет в Java сложилось много нетривиальных решений – например, фреймворк log4j для логирования. На примере контейнерных решений вроде OpenShift можно убедиться, что теперь снова принято работать с stdout и stderr. Нет необходимости внедрять изощренные решения для логирования на уровне языка. Этот пример позволяет судить, как DevOps и новые среды времени выполнения меняют правила игры.

Типичный docker-образ на Go docker имеет размер около 15 MB; сравните его с образом для JVM на Java, размер которого — около 300 MB. Разница 1 к 10. Java JVM оптимизирована под экономный расход памяти, но все равно требует примерно в 10 раз больше памяти, чем Go.

В Go не так много унаследованных фреймворков, поэтому и зависимостей обычно мало, а код зависимостей входит в состав бинарного файла. Поэтому отпадает необходимость в таких сложных инструментах как Maven. В контейнерной среде релиз нового образа необходим всякий раз, когда меняется одна из зависимостей в цепочке. А значит, на Java Java мы должны обновлять такие контейнеры достаточно часто. Хуже того, зависимости обычно запрятаны где-то глубоко.

Java и Scala – это языки для объектно-ориентированного программирования. Но при работе в сравнительно простых предметных областях такие решения кажутся мне довольно затратными. «Гибкий» аспект философии Go позволяет организовать разработку не только не хуже, но и гораздо понятнее.

Java известен своим огромным конвейером и множеством инструментов для непрерывной интеграции вроде Jenkins, которые развились вокруг этого конвейера. В Go конвейеры получаются гораздо короче, проще и быстрее – ведь мы получаем бинарные файлы, уже готовые к исполнению.

В 1990-е был настоящий бум серверов приложений Java – считалось, что они обеспечат независимость разработки от операционной системы и аппаратного обеспечения. Читая спецификацию JEE, мы также рассчитывали на простоту удаленных взаимодействий и компонент-ориентированную разработку. Когда я вижу контейнер docker, на котором работают Java-приложения, всегда вспоминаю о новой версии EJB. В принципе, стек Java не упростился, но теперь он упакован в контейнер. Такая упаковка даром не дается, поскольку добавляется еще один уровень сложности; вы с ним познакомитесь, как только попробуете отладить сеть такого docker-контейнера.

Go docker – вариант для масштабирования сервисов, но сложную среду времени исполнения он не спасает. Если у вас всего один простой сервис, то простые бинарные файлы Go можно выполнять прямо на хосте. Если же речь идет о более сложном приложении, то сервисы можно положить, например, в контейнер и запускать их в PaaS-среде вроде OpenShift. Чтобы протестировать сервис на ноутбуке разработчика, контейнер не нужен, всяческая связанная с ним магия – тоже.

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

Go, где вариантов не так много, помогает быстрее освоиться с решением тем разработчикам, которые сами его не писали. Не требуется углубляться в философию разработки. Разумеется, всегда можно реализовывать сервисы и в более простом стиле на более насыщенных языках, например, на Java или Scala, но в данном случае нужно научиться самоограничению, обсуждать все детали с командой – соответственно, микросервисная архитектура обрастает огромной документацией.

Мне кажется, Go идеально подходит для реализации микросервисов. Почему же этот язык так медленно усваивается в сообществе разработчиков?

Думаю, просто никто не любит резких изменений. Мы попробовали изменить всего одно измерение в многомерном мире программирования. Это измерение – размер сервиса. По моему опыту, изменить одно измерение еще недостаточно, чтобы пошла эволюция. Поскольку все измерения взаимосвязаны, они влияют друг на друга. Решив упростить приложение, переделав его в виде микросервисов, мы должны также упростить и исполняющую среду, и язык программирования, с которым работаем. Иначе получим лишь новую головную боль – например, придется управлять сразу множеством JVM, которые занимают кучу места в памяти и запускаются довольно медленно. Либо получим множество мелких объектно-ориентированных решений, которые будут распределенными, а значит – более сложными. Наконец, мы просто запутаемся со множеством сервисов, образующих огромное дерево зависимостей.

По-моему, не меняя всех измерений сразу, мы словно пересаживаемся с лошади на машину, но берем с собой седло и шпоры.

Давайте не просто писать микросервисы, но и адаптировать под них всю архитектуру. Go отлично для этого подходит. Если предметная область сервиса расширится, вы всегда сможете дополнительно воспользоваться Java или Scala.
Актуальность книги

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

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

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

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


  1. mungobungo
    24.06.2016 16:47
    +6

    Напилить микросервисы это не самая сложная задача.

    Потому что буквально после двух простых рест сервисов возникнут следующие вопросы

    1) Service discovery.
    client side? server side? Как у Go с готовыми библиотеками под это дело? А такими которые уже опробованы в продакешене? А такими, которые опробованы в продакшене крупными компаниями аля нетфликс?
    Понятное дело что можно использовать клиентов для Consul или Eureka.

    2) Circuit breaker
    Вот тот вот сервис подтупливает и отвечает по 1000 секунд вместо 5. Из-за этого все сервисы, которые на него завязаны тоже начинают тупить. Есть ли готовый продакшен фреймворк для go в этом случае? (Hystrix с страницей отчета)

    3) Нужно сделать CRUD over REST, с пагинацией, с базовыми селектами.
    Есть ли удобные и проверенные временем фреймворки для Go под это дело?

    4) OAuth и сотоварищи. Насколько просто повесить на Go endpoint-ы авторизацию и аутентификацию?

    5) Tracebility. Есть ли возможность *просто* отследить все сервисы через которые прошел запрос? Так, чтобы без руками пробрасывать UUID correlationID в заголовках или теле запроса?

    6) Логирование. Stdout, конечно круто, а что насчет простой интеграции с ELK?

    В Java мире эти вопросы закрыты более менее нормально.
    Проблема не в создании микросервисов, а в оркестровке и отладке зоопарка из 20 хотя бы сервисов, каждый из которых имеет по 3-4 инстанса.
    И понятное дело что в ИТ все вопросы решаемы. Но количество приседаний, которые необходимо для этого сделать очень отличатся.

    И раз мы уже пытаемся минимизировать размер docker образа, почему бы не воспользоваться
    Erlang docker 17MB?

    В котором есть let it fail, supervision trees, и отличный веб-серве cowboy.


    1. indestructable
      24.06.2016 19:54
      +4

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


    1. indestructable
      24.06.2016 20:00
      +2

      Ну если серьезно, то ответ есть в начале статьи:

      >> Поставьте эти словечки себе в профиль – и вас сразу начнут осаждать рекрутеры


      1. voidnugget
        24.06.2016 21:33
        +1

        Поставьте эти словечки себе в профиль – и вас сразу начнут осаждать рекрутеры


        C глупыми и бесполезными предложениями.
        DevOps/Микросервисы — это удел запада, а наши потуги внедрять всё это — довольно посредственны.
        Пока не решится вопрос с контролем качества и общей организацией работ, а в 80% случаев их нет, до тех пор и думать про какой-либо DevOps и Микросервисы не то что бесполезно, а просто вредно, и для проектов, и для их руководства.


        1. indestructable
          24.06.2016 23:01

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


          1. SirEdvin
            24.06.2016 23:12

            Если что, я имел ввиду, что крайне странно сравнивать гигантские фреймворки для всего и солянку из маленьких библиотек


    1. lain8dono
      25.06.2016 07:36

      0) Ещё до написания первых двух сервисов стоит глянуть https://github.com/golang/go/wiki/Projects и присмотреть то, что вам надобно. А ещё убедиться, что golang именно то, что вам нужно для вашей задачи.


      1) circuit
      3) gin + gorm например
      4) https://github.com/golang/go/wiki/Projects#authentication
      5) А почему пробрасывать руками это сложно? Лично я так бы и сделал. Один раз написал бы middleware, который за пару-тройку строчек добавляет заголовок и плюётся в какой-либо message queue.
      6) https://github.com/golang/go/wiki/Projects#logging-and-monitoring


      Но я могу быть не прав в контексте ваших задач. Мои задачи go решает. Например я больше занимаюсь кодом, который вместо масштабирования вверх и вширь должен использовать существующие ресурсы без фанатизма (и диск и память и cpu). Вплоть до того, что docker это кривой оверхед для меня. С другой стороны я не побоюсь использовать няшный Си и ассембрер, если в этом возникнет крайняя необходимость.


  1. SirEdvin
    24.06.2016 23:11
    +1

    Почему в статье сравнивают Java EE стек и программы на го, которые состоят из кучи маленьких решений?

    Ну и фраза про «go потребляет в 10 раз меньше памяти чем Java» шикарна, конечно.


    1. grossws
      25.06.2016 00:27

      Особенно учитывая, что при использовании docker слой с jre/jdk размером в 300 MiB будет скачиваться и храниться один раз на хост, что, в общем, копейки.


    1. avost
      26.06.2016 18:47
      -1

      Потому, что они выполняют в итоге одни и те же функции?


  1. setevoy4
    26.06.2016 21:03

    > Типичный docker-образ на Go docker имеет размер около 15 MB; сравните его с образом для JVM на Java, размер которого — около 300 MB. Разница 1 к 10.

    Если речь идёт о разнизе в размере, то разница никак не в 10 раз…

    > Чтобы протестировать сервис на ноутбуке разработчика, контейнер не нужен, всяческая связанная с ним магия – тоже.

    Docker даёт возможность унифицировать дев- и прод- окружения, запуская сервис в «одинаковом» (простите) окружении. Хотя, конечно, смотря что понимать под «протестировать».


  1. Duke_kz
    26.06.2016 21:03

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


    1. ph_piter
      27.06.2016 11:14

      Да, возможно, написали в личку.