Php микросервисы: Готовим простой блог на микросервисах, пишем свой микрофреймворк на php и запускаем все на Docker с примерами
Микросервисы для начинающих / Хабр
Оглядываясь примерно на пять лет назад в прошлое, можно заметить, насколько сильно с тех пор изменилось отношение к архитектуре микросервисов. Поначалу они были чрезвычайно популярны. После успеха Netflix, Amazon и Gilt.com разработчики решили, что де-факто разработка микросервисов не отличается от разработки приложений. Теперь же все поняли, что микросервисы представляют из себя новый архитектурный стиль, который эффективен для решения определенных задач, имеет свои плюсы и минусы.
Чтобы понять, что такое микросервисы и в каких случаях их следует использовать, мы обратились к Джейме Буэльта (Jaime Buelta), автору книги «Hands-On Docker for Microservices with Python». Он рассказал о преимуществах этой архитектуры, а также поделился рекомендациями для разработчиков, планирующих перейти на нее с монолитов.
Преимущества и риски
Традиционное монолитное приложение объединяет все свои возможности в едином связанном модуле. В случае микросервисов все наоборот. Приложение делится на более мелкие автономные службы, которые можно независимо развертывать, обновлять и заменять. Каждый микросервис создается для одной бизнес-цели и может взаимодействовать с другими микросервисами с помощью простых механизмов.
Буэльта объясняет: «Микросервисная архитектура — это способ структурирования системы, при которой несколько независимых сервисов общаются друг с другом определенным образом (обычно это происходит с помощью web-сервисов RESTful). Ключевая особенность состоит в том, что каждый микросервис способен обновляться и развертываться независимо от остальных».
Архитектура микросервисов определяет не только то, как вы создаете свое приложение, но и то, как организована ваша команда.
«Одна независимая команда может полностью отвечать за микросервис. Это позволяет организациям расти, не сталкивая разработчиков друг с другом», — объясняет Буэльта.
Одно из основных преимуществ микросервисов заключается в том, что они позволяют внедрять нововведения без особого влияния на систему в целом. С помощью микросервисов вы можете выполнять горизонтальное масштабирование, иметь четкие границы модулей, использовать разнообразные технологии и вести параллельную разработку.
На вопрос о рисках, связанных с микросервисами, Буэльта ответил: «Главная сложность при внедрении архитектуры (особенно при переходе с монолита) заключается в создании дизайна, в котором сервисы действительно будут независимыми. Если этого не удастся добиться, то межсервисные связи станут сложнее, что приведет к дополнительным расходам. Микросервисам нужны профессионалы, которые сформируют направления развития в долгосрочной перспективе. Я рекомендую организациям, которые хотят перейти на такую архитектуру, назначить кого-то ответственным за «общую картину». На микросервисы нужно смотреть более широко», — считает Джейме.
Переход от монолита к микросервисам
Мартин Фаулер, известный автор и консультант по программному обеспечению, советует придерживаться принципа «сначала — монолит». Это связано с тем, что использовать микросервисную архитектуру с самого начала разработки рискованно, поскольку в большинстве случаев она подходит только для сложных систем и больших команд разработчиков.
«Основной критерий, который должен побуждать вас к переходу на новую архитектуру — это численность вашей команды. Небольшим группам не стоит этого делать. В подобных условиях разработчики и так понимают все, что происходит с приложением, и всегда могут задать уточняющий вопрос коллеге. Монолит отлично работает в этих ситуациях, и поэтому практически каждая система начинается с него», — считает Джейме. Это подтверждает «правило двух пицц» Amazon, согласно которому команду, ответственную за один микросервис, можно прокормить двумя пиццами — иначе она слишком большая.
«По мере роста компании и увеличения команд разработчиков может потребоваться лучшая координация. Программисты начинают часто мешать друг другу. Понять цель конкретного фрагмента кода становится сложнее. В таких случаях переход на микросервисы имеет смысл — это поможет разделить обязанности и внести ясность в общую картину системы. Каждая команда может ставить свои собственные цели и работать в основном самостоятельно, выдавая понятный внешний интерфейс. Однако, чтобы такой переход имел смысл, разработчиков должно быть много», — добавляет Буэльта.
Рекомендации по переходу на микросервисы
Отвечая на вопрос о том, какие практические рекомендации могут использовать разработчики при переходе на микросервисы, Буэльта заявил: «Ключом к успешной архитектуре микросервисов является то, что каждый сервис должен быть максимально независим».
Возникает вопрос: «Как вы можете сделать сервисы независимыми?». Лучший способ обнаружить взаимозависимость системы — подумать о новых возможностях: «Если вы хотите добавить новую функцию, можно ли будет ее реализовать, изменив лишь один сервис? Какие виды функций потребуют координации нескольких микросервисов? Они будут использоваться часто или редко? Невозможно создать идеальный дизайн, но, по крайней мере, с его помощью можно принимать правильные и обоснованные решения», — объясняет Буэльта.
Джейме советует переходить на архитектуру правильно, чтобы потом все не переделывать. «После завершения перехода изменить границы микросервисов будет тяжелее. Стоит уделить побольше времени на начальную фазу проекта», — добавляет он.
Переход с одного шаблона проектирования на другой — это серьезный шаг. Мы спросили, с какими проблемами Джейме и его команда сталкивались во время миграции на микросервисы, на что он ответил:
«На деле основные трудности связаны с людьми. Эти проблемы, как правило, недооценивают, но переход на микросервисы фактически меняет способ работы разработчиков. Задача не из легких!». Он добавляет: «Я лично сталкивался с подобными проблемами. Например, мне приходилось обучать и давать советы разработчикам. Особенно важно объяснять, почему необходимы те или иные изменения. Это помогает людям понять причины внедрения всех нововведений, которые могут прийтись им не по душе.
При переходе от монолитной архитектуры много сложностей может возникнуть при развертывании приложения, которое раньше выпускалось в виде единого модуля. Оно требует более тщательного анализа для обеспечения обратной совместимости и минимизации рисков. Справиться с этой задачей порой очень нелегко».
Причины выбора Docker, Kubernetes и Python в качестве технологического стека
Мы спросили Буэльту, какие технологии он предпочитает для внедрения микросервисов. Касательно выбора языка ответ оказался прост: «Python для меня — лучший вариант. Это мой любимый язык программирования!.. Этот язык хорошо подходит для микросервисов. Его удобно читать и легко применять. Кроме того, Python обладает широким функционалом для веб-разработки и динамичной экосистемой сторонних модулей для любых потребностей. К этим потребностям относится подключение к другим системам, например, к базам данных, внешним API и т.д.».
Docker часто рекламируется как один из самых важных инструментов для микросервисов. Буэльта объяснил, почему:
«Docker позволяет инкапсулировать и копировать приложение в удобных стандартизированных пакетах. Это уменьшает неопределенность и сложность среды. Также это значительно упрощает переход от разработки к производству приложений. Вдобавок ко всему, уменьшается время использования оборудования. Вы можете разместить несколько контейнеров в разных средах (даже в разных операционных системах) в одной физической коробке или виртуальной машине».
Про Kubernetes:
«Kubernetes позволяет развертывать несколько контейнеров Docker, работающих скоординированным образом. Это заставляет разработчиков мыслить кластеризованно, помня о производственной среде. Также это позволяет определять кластер с помощью кода, чтобы новые развертывания или изменения конфигурации определялись в файлах. Все это делает возможными методы наподобие GitOps (о них я писал в своей книге), при этом сохраняя полную конфигурацию в системе управления версиями. Каждое изменение вносится определенным и обратимым образом, поскольку оно представляет из себя регулярное git-слияние. Благодаря этому можно очень легко восстанавливать или дублировать инфраструктуру».
«Придется потратить время, чтобы обучиться Docker и Kubernetes, но это того стоит. Оба инструмента очень мощные. К тому же, они поощряют вас работать таким образом, чтобы избежать проблем при производстве», — считает Буэльта.
Многоязычные микросервисы
При разработке микросервисов можно использовать разнообразные технологии, поскольку за каждый из них в идеале отвечает независимая команда. Буэльта поделился своим мнением о многоязычных микросервисах: «Многоязычные микросервисы — это здорово! Это одно из основных преимуществ архитектуры. Типичный пример многоязычного микросервиса — перенос устаревшего кода, написанного на одном языке, на новый. Микросервис может заменить собой любой другой, который предоставляет тот же внешний интерфейс. При этом его код будет совершенно иным. К примеру, я переходил со старых приложений PHP, заменяя их аналогами, написанными на Python». Джейме добавил: «Работа с двумя или более платформами одновременно поможет лучше разобраться в них и понимать, в каких случаях их лучше использовать».
Хотя возможность использовать многоязычные микросервисы — это большое преимущество архитектуры, оно также может увеличить операционные издержки. Буэльта советует: «Надо знать меру. Нет смысла каждый раз использовать новый инструмент и лишать команды возможности делиться знаниями друг с другом. Конкретные цифры могут зависеть от размера компании, но, как правило, нет смысла использовать больше двух или трех разных языков без серьезной на то причины. Не надо раздувать стек технологий — тогда разработчики смогут делиться знаниями и начнут использовать имеющиеся инструменты наиболее эффективно».
Об авторе
Джейме Буэльта (Jaime Buelta) — профессиональный программист и Python-разработчик, который за свою многолетнюю карьеру познакомился со множеством различных технологий. Он разрабатывал программное обеспечение для различных областей и отраслей, включая аэрокосмическую, сетевую и коммуникационную, а также промышленные системы SCADA, онлайн-сервисы для видеоигр и финансовые сервисы.
В составе различных компаний он имел дело с такими функциональными областями, как маркетинг, менеджмент, продажи и геймдизайн. Джейме является ярым сторонником автоматизации и хочет, чтобы всю тяжелую работу выполняли компьютеры, позволив людям сосредоточиться на более важных вещах. В настоящее время он живет в Дублине и регулярно выступает на конференциях PyCon в Ирландии.
Микросервисы (Microservices) / Хабр
От переводчика: некоторые скорее всего уже читали этот титанический труд от Мартина Фаулера и его коллеги Джеймса Льюиса, но я все же решил сделать перевод этой статьи. Тренд микросервисов набирает обороты в мире enterprise разработки, и эта статья является ценнейшим источником знаний, по сути выжимкой существующего опыта работы с ними.
Термин «Microservice Architecture» получил распространение в последние несколько лет как описание способа дизайна приложений в виде набора независимо развертываемых сервисов. В то время как нет точного описания этого архитектурного стиля, существует некий общий набор характеристик: организация сервисов вокруг бизнес-потребностей, автоматическое развертывание, перенос логики от шины сообщений к приемникам (endpoints) и децентрализованный контроль над языками и данными.
«Микросервисы» — еще один новый термин на шумных улицах разработки ПО. И хотя мы обычно довольно настороженно относимся ко всем подобным новинкам, конкретно этот термин описывает стиль разработки ПО, который мы находим все более и более привлекательным. За последние несколько лет мы видели множество проектов, использующих этот стиль, и результаты до сих пор были весьма позитивными. Настолько, что для большинства наших коллег этот стиль становится основным стилем разработки ПО. К сожалению, существует не так много информации, которая описывает, чем же являются микросервисы и как применять их.
Если коротко, то архитектурный стиль микросервисов — это подход, при котором единое приложение строится как набор небольших сервисов, каждый из которых работает в собственном процессе и коммуницирует с остальными используя легковесные механизмы, как правило HTTP. Эти сервисы построены вокруг бизнес-потребностей и развертываются независимо с использованием полностью автоматизированной среды. Существует абсолютный минимум централизованного управления этими сервисами. Сами по себе эти сервисы могут быть написаны на разных языках и использовать разные технологии хранения данных.
Для того, чтобы начать рассказ о стиле микросервисов, лучше всего сравнить его с монолитом (monolithic style): приложением, построенном как единое целое. Enterprise приложения часто включают три основные части: пользовательский интерфейс (состоящий как правило из HTML страниц и javascript-а), база данных (как правило реляционной, со множеством таблиц) и сервер. Серверная часть обрабатывает HTTP запросы, выполняет доменную логику, запрашивает и обновляет данные в БД, заполняет HTML страницы, которые затем отправляются браузеру клиента. Любое изменение в системе приводит к пересборке и развертыванию новой версии серверной части приложения.
Монолитный сервер — довольно очевидный способ построения подобных систем. Вся логика по обработке запросов выполняется в единственном процессе, при этом вы можете использовать возможности вашего языка программирования для разделения приложения на классы, функции и namespace-ы. Вы можете запускать и тестировать приложение на машине разработчика и использовать стандартный процесс развертывания для проверки изменений перед выкладыванием их в продакшн. Вы можете масштабировать монолитное приложения горизонтально путем запуска нескольких физических серверов за балансировщиком нагрузки.
Монолитные приложения могут быть успешными, но все больше людей разочаровываются в них, особенно в свете того, что все больше приложений развертываются в облаке. Любые изменения, даже самые небольшие, требуют пересборки и развертывания всего монолита. С течением времени, становится труднее сохранять хорошую модульную структуру, изменения логики одного модуля имеют тенденцию влиять на код других модулей. Масштабировать приходится все приложение целиком, даже если это требуется только для одного модуля этого приложения.
Эти неудобства привели к архитектурному стилю микросервисов: построению приложений в виде набора сервисов. В дополнение к возможности независимого развертывания и масштабирования каждый сервис также получает четкую физическую границу, которая позволяет разным сервисам быть написанными на разных языках программирования. Они также могут разрабатываться разными командами.
Мы не утверждаем, что стиль микросервисов это инновация. Его корни уходят далеко в прошлое, как минимум к принципам проектирования, использованным в Unix. Но мы тем не менее считаем, что недостаточно людей принимают во внимание этот стиль и что многие приложения получат преимущества если начнут применять этот стиль.
Свойства архитектуры микросервисов
Мы не можем сказать, что существует формальное определение стиля микросервисов, но мы можем попытаться описать то, что мы считаем общими характеристиками приложений, использующих этот стиль. Не всегда они встречаются в одном приложении все сразу, но, как правило, каждое подобное приложение включает в себя большинство этих характеристик. Мы попробуем описать то, что мы видим в наших собственных разработках и в разработках известных нам команд.
Разбиение через сервисы
В течение всего срока нашего пребывания в индустрии мы видим желание строить системы путем соединения вместе различных компонент, во многом так же, как это происходит в реальном мире. За последние пару десятков лет мы видели большой рост набора библиотек, используемых в большинстве языков программирования.
Говоря о компонентах, мы сталкиваемся с трудностями определения того, что такое компонент. Наше определение такого: компонент — это единица программного обеспечения, которая может быть независимо заменена или обновлена.
Архитектура микросервисов использует библиотеки, но их основной способ разбиения приложения — путем деления его на сервисы. Мы определяем библиотеки как компоненты, которые подключаются к программе и вызываются ею в том же процессе, в то время как сервисы — это компоненты, выполняемые в отдельном процессе и коммуницирующие между собой через веб-запросы или remote procedure call (RPC).
Главная причина использования сервисов вместо библиотек — это независимое развертывание. Если вы разрабатываете приложение, состоящее из нескольких библиотек, работающих в одном процессе, любое изменение в этих библиотеках приводит к переразвертыванию всего приложения. Но если ваше приложение разбито на несколько сервисов, то изменения, затрагивающие какой-либо из них, потребуют переразвертывания только изменившегося сервиса. Конечно, какие-то изменения будут затрагивать интерфейсы, что, в свою очередь, потребует некоторой координации между разными сервисами, но цель хорошей архитектуры микросервисов — минимизировать необходимость в такой координации путем установки правильных границ между микросервисами, а также механизма эволюции контрактов сервисов.
Другое следствие использования сервисов как компонент — более явный интерфейс между ними. Большинство языков программирования не имеют хорошего механизма для объявления Published Interface. Часто только документация и дисциплина предотвращают нарушение инкапсуляции компонентов. Сервисы позволяют избежать этого через использование явного механизма удаленных вызовов.
Тем не менее, использование сервисов подобным образом имеет свои недостатки. Удаленные вызовы работают медленнее, чем вызовы в рамках процесса, и поэтому API должен быть менее детализированным (coarser-grained), что часто приводит к неудобству в использовании. Если вам нужно изменить набор ответственностей между компонентами, сделать это сложнее из-за того, что вам нужно пересекать границы процессов.
В первом приближении мы можем наблюдать, что сервисы соотносятся с процессами как один к одному. На самом деле сервис может содержать множество процессов, которые всегда будут разрабатываться и развертываться совместно. Например, процесс приложения и процесс базы данных, которую использует только это приложение.
Организация вокруг потребностей бизнеса
Когда большое приложение разбивается на части, часто менеджмент фокусируется на технологиях, что приводит к образованию UI команды, серверной команды и БД команды. Когда команды разбиты подобным образом, даже небольшые изменения отнимают много времени из-за необходимости кросс-командного взаимодействия. Это приводит к тому, что команды размещают любую логику на тех слоях, к которым имеют доступ. Закон Конвея (Conway’s Law) в действии.
«Любая организация, которая проектирует какую-то систему (в широком смысле) получит дизайн, чья структура копирует структуру команд в этой организации»
— Melvyn Conway, 1967
Закон Конвея (Conway’s Law) в действии
Микросервисный подход к разбиению подразумевает раазбиение на сервисы в соответствии с потребностями бизнеса. Такие сервисы включают в себя полный набор технологий, необходимых для этой бизнес-потребности, в том числе пользовательский интерфейс, хранилице данных и любые внешние взаимодействия. Это приводит к формированию кросс-функциональных команд, имеющих полный набор необходимых навыков: user-experience, базы данных и project management.
Сервисные границы, подкрепленные границами команд
Одна из компаний, организованных в этом стиле — www.comparethemarket.com. Кросс-фунциональные команды отвечают за построение и функционирование каждого продукта и каждый продукт разбит на несколько отдельных сервисов, общающихся между собой через шину сообщений.
Крупные монолитные приложения тоже могут быть разбиты на модули вокруг бизнес потребностей, хотя обычно этого не происходит. Безусловно, мы рекомендуем большим командам строить монолитные приложения именно таким образом. Основная проблема здесь в том, что такие приложения имеют тенденцию к организации вокруг слишком большого количества контекстов. Если монолит охватывает множество контекстов, отдельным членам команд становится слишком сложно работать с ними из-за их большого размера. Кроме того, соблюдение модульных границ в монолитном приложении требует существенной дисциплины. Явно очерченные границы компонент микросервисов упрощает поддержку этих границ.
Насколько большими должны быть микросервисы?
Хотя термин «Микросервис» стал популярным названием для этого архитектурного стиля, само имя приводит к чрезмерному фокусу на размере сервисов и спорам о том, что означает приставка «микро». В наших разговорах с теми, кто занимался разбиением ПО на микросервисы, мы видели разные размеры. Наибольший размер был у компаний, следовавших правилу «Команда двух пицц» (команда, которую можно накормить двумя пиццами), т.е. не более 12 человек (прим. перев.: следуя этому правилу, я в команде должен быть один). В других компаниях мы видели команды, в которых шестеро человек поддерживали шесть сервисов.
Это приводит к вопросу о том, есть ли существенная разница в том, сколько человек должно работать на одном сервисе. На данный момент мы считаем, что оба этих подхода к построению команд (1 сервис на 12 человек и 1 сервис на 1 человека) подходят под описание микросервисной архитектуры, но возможно мы изменим свое мнение в будущем. (прим. перев.: со времен статьи появилось множество других статей, развивающих эту тему; наиболее популярным сейчас считается мнение о том, что сервис должен быть настолько большим, чтобы он мог полностью «уместиться в голове разработчика», независимо от количества строк кода).
Продукты, а не проекты
Большиство компаний по разработке ПО, которые мы видим, используют проектную модель, в которой целью является разработка некой части функциональности, которая после этого считается завершенной. После завершения эта часть передается команде поддержки и проектная команда распускается.
Сторонники микросервисов сторонятся этой модели, утверждая, что команда должна владеть продуктом на протяжении всего срока его жизни. Корни этого подхода уходят к Амазону, у компании есть правило «вы разработали, вам и поддерживать», при котором команда разработки берет полную ответственность за ПО в продакшне. Это приводит к тому, что разработчики регулярно наблюдают за тем, как их продукт ведет себя в продакшне, и больше контактируют с пользователями, т.к. им приходится брать на себя как минимум часть обязанностей по поддержке.
Мышление в терминах продукта устанавливает связь с потребностями бизнеса. Продукт — это не просто набор фич, которые необходимо реализовать. Это постоянные отношения, цель которых — помочь пользователям увеличить их бизнес-возможности.
Конечно, этого можно также достичь и в случае с монолитным приложением, но высокая гранулярность сервисов упрощает установку персональных отношений между разработчиками сервиса и его пользователями.
Умные приемники и глупые каналы передачи данных (Smart endpoints and dumb pipes)
При выстраивании коммуникаций между процессами мы много раз были свидетелями того, как в механизмы передачи данных помещалась существенная часть логики. Хорошим примером здесь является Enterprise Service Bus (ESB). ESB-продукты часто включают в себя изощренные возможности по передаче, оркестровке и трансформации сообщений, а также применению бизнес-правил.
Комьюнити микросервисов предпочитает альтернативный подход: умные приемники сообщений и глупые каналы передачи. Приложения, построенные с использованием микросервисной архитектуры, стремятся быть настолько незавимыми (decoupled) и сфокусировнными (cohesive), насколько возможно: они содержат собственную доменную логику и выступают больше в качестве фильтров в классическом Unix-овом смысле — получают запросы, применяют логику и отправляют ответ. Вместо сложных протоколов, таких как WS-* или BPEL, они используют простые REST-овые протоколы.
Два наиболее часто используемых протокола — это HTTP запросы через API ресурса и легковесный месседжинг. Лучшее выражение первому дал Ian Robinson: «Be of the web, not behind the web».
Команды, практикующие микросервисную архитектуру, используют те же принципы и протоколы, на которых построена всемирная паутина (и, по сути, Unix). Часто используемые ресурсы могут быть закешированы с очень небольшими усилиями со стороны разработчиков или IT-администраторов.
Второй часто используемый инструмент коммуникации — легковесная шина сообщений. Такая инфраструктура как правило не содержит доменной логики — простые реализации типа RabbitMQ или ZeroMQ не делают ничего кроме предоставления асинхронной фабрики. Логика при этом существует на концах этой шины — в сервисах, которые отправляют и принимают сообщения.
В монолитном приложении компоненты работают в одном процессе и коммуницируют между собой через вызов методов. Наибольшая проблема в смене монолита на микросервисы лежит в изменении шаблона коммуникации. Наивное портирование один к одному приводит к «болтливым» коммуникациям, которые работают не слишком хорошо. Вместо этого вы должны уменьшить количество коммуникаций между модулями.
Децентрализованное управление
Одним из следствий централизованного управления является тенденция к стандартизации используемых платформ. Опыт показывает, что такой подход слишком сильно ограничивает выбор — не всякая проблема является гвоздем и не всякое решение является молотком. Мы предпочитаем использовать правильный инструмент для каждой конкретной работы. И хотя монолитные приложения тоже в некоторых случаях могут быть написаны с использованием разных языков, это не является стандартной практикой.
Разбивая монолит на сервисы, мы имеем выбор, как построить каждый из них. Хотите использовать Node.js для простых страничек с отчетами? Пожалуйста. C++ для real-time приложений? Отлично. Хотите заменить БД на ту, которая лучше подходит для операций чтения вашего компонента? Ради бога.
Конечно, только потому что вы можете делать что-то, не значит что вы должны это делать. Но разбиение системы подобным образом дает вам возможность выбора.
Команды, разрабатывающие микросервисы, также предпочитают иной подход к стандартизации. Вместо того, чтобы использовать набор предопределенных стандартов, написанных кем-то, они предпочитают идею построения полезных инструментов, которые остальные девелоперы могут использовать для решения похожих проблем. Эти инструменты как правило вычленены из кода одного из проектов и расшарены между разными командами, иногда используя при этом модель внутреннего опен-сорса. Теперь, когда git и github стали де-факто стандартной системой контроля версий, опен-сорсные практики становятся все более и более популярными во внутренних проектах компаний.
Netflix — хороший пример организации, которая следует этой философии. Расшаривание полезного и, более того, протестированного на боевых серверах кода в виде библиотек побуждает остальных разработчиков решать схожие проблемы схожим путем, оставляя тем не менее возможность выбора другого подхода при необходимости. Общие библиотеки имеют тенденцию быть сфокусированными на общих проблемах, связанных с хранением данных, межпроцессорным взаимодействием и автоматизацией инфраструктуры.
Комьюнити микросервисов ценит сервисные контракты, но не любит оверхеды и поэтому использует различные пути управления этими контрактами. Такие шаблоны как Tolerant Reader и Consumer-Driven Contracts часто используются в микросервисах, что позволяет им эволюционировать независимо. Проверка Consumer-Driven контрактов как часть билда увеличивает уверенность в правильности функционирование сервисов. Мы знаем команду из Австралии, которая использует этот подход для проверки контрактов. Это стало частью их процесса сборки: сервис собирается только до того момента, который удовлетворяет требованиям контракта — элегантный способ обойти диллему YAGNI.
Пожалуй наивысшая точка в практике децентрализованного управления — это метод, популизированный Амазоном. Команды отвечают за все аспекты ПО, которое они разрабатывают, включая поддержку его в режиме 24/7. Подобная деволюция уровня ответственности совершенно точно не является нормой, но мы видим все больше и больше компаний, передающий ответственность командам разработчиков. Netflix — еще одна компания, практикующая это. Пробуждение в 3 часа ночи — очень сильный стимул к тому, чтобы уделять большое внимание качеству написанного кода.
Микросервисы и SOA
Когда мы разговариваем о микросервисах, обычно возникает вопрос о том, не является ли это обычным Service Oriented Architecture (SOA), который мы видели десять лет назад. В этом вопросе есть здравое зерно, т.к. стиль микросервисов очень похож на то, что продвигают некоторые сторонники SOA. Проблема, тем не менее, в том, что термин SOA имеет сликом много разных значений и, как правило, то, что люди называют «SOA» существенно отличается от стиля, описанного здесь, обычно из-за чрезмерного фокуса на ESB, используемом для интеграции монолитных приложений.
В частности, мы видели так много неудачных реализаций SOA (начиная с тенденции прятать сложность за ESB, заканчивая провалившимися инциативами длительностью несколько лет, которые стоили миллионы долларов и не принесли никакой пользы), что порой слишком сложно абстрагироваться от этих проблем.
Безусловно, многие практики, используемые в микросервисах, пришли из опыта интеграции сервисов в крупных организациях. Шаблон Tolerant Reader — один из примеров. Другой пример — использование простых протоколов — возник как реакция на централизованные стандарты, сложность которых просто захватывает дух.
Эти проблемы SOA привели к тому, что некоторые сторонники микросервисов отказываются от термина «SOA», хотя другие при этом считают микросервисы одной из форм SOA, или, возможно, правильной реализацией SOA. В любом случае, тот факт, что SOA имеет разные значения, означает, что полезно иметь отдельный термин для обозначения этого архитектурного стиля.
Множество языков, множество возможностей
Рост платформы JVM — один из последних примеров смешивания языков в рамках единой платформы. Переход к более высокоуровневым языкам для получения преимуществ, связанных с использованием высокоуровневых абстракций, был распространенной практикой в течение десятилетий. Точно так же, как и переход «к железу» для написания высокопроизводительного кода.
Тем не менее, множество монолитных приложений не требуют такого уровня оптимизации производительности и высокоуровневых возможностей DSL-подобных языков. Вместо этого, монолиты как правило используют единый язык и склонны к ограничению количества используемых технологий.
Децентрализованное управление данными
Децентрализованное управление данными предстает в различном виде. В наиболее абстрактном смысле это означает, что концептуальная модель мира у разных систем будет отличаться. Это обычная проблема, возникающая при интеграции разных частей больших enterprise-приложений: точка зрения на понятие «Клиент» у продажников будет отличаться от таковой у команды техподдержки. Некоторые атрибуты «Клиента» могут присутствовать в контексте продажников и отсутствовать в контексте техподдержки. Более того, атрибуты с одинаковым названием могут иметь разное значение.
Эта проблема встречается не только у разных приложений, но также и в рамках единого приложения, особенно в тех случаях когда это приложение разделено на отдельные компоненты. Эту проблему хорошо решает понятие Bounded Context из Domain-Driven Design (DDD). DDD предлагает делить сложную предметную область на несколько контекстов и мапить отношения между ними. Этот процесс полезен как для монолитной, так и для микросервисной архитектур, но между сервисами и контекстами существует естественная связь, которая помогает прояснять и поддерживать границы контекстов.
Кроме децентрализации принятия решений о моделировании предметной области, микросервисы также способствуют децентрализации способов хранения данных. В то время как монолитные приложения склонны к использованию единственной БД для хранения данных, компании часто предпочитают использовать единую БД для целого набора приложений. Такие решения, как правило, вызваны моделью лицензирования баз данных. Микросервисы предпочитают давать возможность каждому сервису управлять собственной базой данных: как создавать отдельные инстансы общей для компании СУБД, так и использовать нестандартные виды баз данных. Этот подход называется Polyglot Persistence. Вы также можете применять Polyglot Persistence в монолитных приложениях, но в микросервисах такой подход встречается чаще.
Децентрализация ответственности за данные среди микросервисов оказывает влияние на то, как эти данные изменяются. Обычный подход к изменению данных заключается в использовании транзакций для гарантирования консистентности при изменении данных, находящихся на нескольких ресурсах. Такой подход часто используется в монолитных приложениях.
Подобное использование транзакций гарантирует консистентность, но приводит к существенной временной зависимости (temporal coupling), которая, в свою очередь, приводит к проблемамм при работе с множеством сервисов. Распределенные транзакции невероятно сложны в реализации и, как следствие, микросервисная архитектура придает особое значению координации между сервисами без использования транзакций с явным обозначением того, что консистентность может быть только итоговой (eventual consistency) и возникающие проблемы решаются операциями компенсации.
Управление несогласованностями подобным образом — новый вызов для многих команд разработки, но это часто соответствует практикам бизнеса. Часто компании стремятся как можно быстрее реагировать на действия пользователя и имеют процессы, позвояющие отменить действия пользователей в случае ошибки. Компромисс стоит того до тех пор, пока стоимость исправления ошибки меньше стоимости потерь бизнеса при использовании сценариев, гарантирующих консистентность.
Стандартны, проверенные в бою, vs навязанные стандарты
Команды, использующие микросервисную архитектуру, склонны избегать жестких стандартов, установленных группами системных архитекторов. Они также склонны использовать и даже продвигать открытые стандарты типа HTTP и ATOM.
Ключевое отличие в том, как эти стандарты разрабатываются и как они проводятся в жизнь. Стандарты, управляемые группами вроде IETF, становятся стандартами только тогда, когда находятся несколько реализаций в успешных open-source проектах.
Это отличает их от стандартов в корпоративном мире, которые часто разрабатываются группами людей с небольшим опытом реальной разработки или имеют слишком сильное влияние, оказываемое вендорами.
Автоматизация инфраструктуры
Техники автоматизации инфраструктуры сильно эволюционировали за последние несколько лет. Эволюция облака в целом и AWS в частности уменьшила операционную сложность построения, разворачивания и функционирования микросервисов.
Множество продуктов и систем, использующих микросервисную архитектуру, были построены командами с обширным опытом в Continuous Delivery и Continuous Integration. Команды, строящие приложения подобнымм образом, интенсивно используют техники автоматизации инфраструктуры. Это проиллюстрировано на картинке ниже.
Так как эта статья не про Continuous Delivery, мы уделим внимание лишь паре его ключевых моментов. Мы хотим получать как можно больше уверенности в том, что наше приложение работает, поэтому мы запускаем множество автоматических тестов. Для выполнения каждого шага автоматического тестирования приложение разворачивается в отдельной среде, для чего используется автоматического развертывание (automated deployment).
После того как вы инвестировали время и деньги в автоматизацию процесса развертывания монолита, развертывание большего количества приложений (сервисов) уже не видится таким пугающим. Вспомните, что одна из целей Continuous Delivery — это сделать развертывание скучным, так что одно это приложение или три не имеет большого значения.
Другая область, где команды используют интенсивную автоматизацию инфраструктуры, — это управление микросервисами в продакшне. В отличие от процесса развертывания, который, как описано выше, у монолитных приложений не сильно отличается от такового у микросервисов, их способ фунционирования может существенно различаться.
Одним из побочных эффектов автоматизации процесса развертывания является создание удобных инструментов для помощи разработчикам и администраторам (operations folk). Инструменты для управления кодом, развертывания простых сервисов, мониторинга и логирования сейчас довольно распространены. Возможно наилучший пример, который можно найти в сети, — это набор open source инструментов от Netflix, но существуют и другие, к примеру Dropwizard, который мы довольно интенсивно используем.
Проектирование под отказ (Design for failure)
Следствием использования сервисов как компонентов является необходимость проектирования приложений так, чтобы они могли работать при отказе отдельных сервисов. Любое обращение к сервису может не сработать из-за его недоступности. Клиент должен реагировать на это настолько терпимо, насколько возможно. Это является недостатоком микросервисов по сравнению с монолитом, т.к. это вносит дополнительную сложность в приложение. Как следствие, команды микросервисов постоянно думают на тем, как недоступность сервисов должна влиять на user experience. Simian Army от Netflix искуственно вызывает (симулирует) отказы сервисов и даже датацентров в течение рабочего дня для тестирования отказоустойчивости приложения и служб мониторинга.
Подобный вид автоматического тестирования в продакшне позволяет сэмулировать стресс, который ложится на администраторов и часто приводит к работе по выходным. Мы не хотим сказать, что для монолитных приложений не могут быть разработаны изощренные системы мониторинга, только то, что такое встречается реже.
Так как сервисы могут отказать в любое время, очень важно иметь возможность быстро обнаружить неполадки и, если возможно, автоматически восстановить работоспособность сервиса. Микросервисная архитектура делает большой акцент на мониторинге приложения в режиме реального времени, проверке как технических элементов (например, как много запросов в секунду получает база данных), так и бизнес-метрик (например, как много заказов в минуту получает приложение). Семантический мониторинг может предоставить систему раннего предупреждения проблемных ситуаций, позволяя команде разработке подключиться к исследованию проблемы на самых ранних стадиях.
Это особенно важно с случае с микросервисной архитектурой, т.к. разбиение на отдельные процессы и коммуникация через события приводит к неожиданному поведению. Мониторинг крайне важен для выявления нежелательных случаев такого поведения и быстрого их устранения.
Монолиты могут быть построены так же прозначно, как и микросервисы. На самом деле, так они и должны строиться. Разница в том, что знать, когда сервисы, работающие в разных процессах, перестали корректно взаимодействовать между собой, намного более критично. В случае с библиотеками, расположенными в одном процессе, такой вид прозрачности скорее всего будет не так полезен.
Команды микросервисов, как правило, создают изощренные системы мониторинга и логирования для каждого индивидуального сервиса. Примером может служить консоль, показывающая статус (онлайн/офлайн) сервиса и различные технические и бизнес-метрики: текущая пропускная способность, время обработки запроса и т.п.
Синхронные вызовы считаются опасными
Каждый раз когда вы имеете набор синхронных вызовов между сервисами, вы сталкиваетесь с эффектом мультипликации времени простоя (downtime). Время простоя вашей системы становится произведением времени простоя индивидуальных компонент системы. Вы сталкиваетесь с выбором: либо сделать ваши вызовы асинхронными, либо мириться с простоями. К примеру, в www.guardian.co.uk разработчики ввели простое правило — один синхронный вызов на один запрос пользователя. В Netflix же вообще все API являются асинхронными.
Эволюционный дизайн
Те, кто практикует микросервисную архитектуру, обычно много работали с эволюционным дизайном и рассматривают декомпозицию сервисов как дальнейшую возможность дать разработчикам контроль над изменениями (рефакторингом) их приложения без замедления самого процесса разработки. Контроль над изменениями не обязательно означает уменьшение изменений: с правильным подходом и набором инструментов вы можно делать частые, быстрые, хорошо контролируемые изменения.
Каждый раз когда вы пытаетесь разбить приложение на компоненты, вы сталкиваетесь с необходимостью принять решение, как именно делить приложение. Есть ли какие-то принципы, указывающие, как наилучшим способом «нарезать» наше приложение? Ключевое свойство компонента — это независимость его замены или обновления, что подразумевает наличие ситуаций когда его можно переписать с нуля без затрагивания взаимодействующих с ним компонентов. Многие команды разработчиков идут еще дальше: они явным образом планируют, что множество сервисов в долгосрочной перспективе не будет эволюционировать, а будут просто выброшены на свалку.
Веб-сайт Guardian — хороший пример приложения, которое было спроектировано и построено как монолит, но затем эволюционировало в сторону микросервисов. Ядро сайта все еще остается монолитом, но новые фичи добавляются путем построения микросервисов, которые используют API монолита. Такой подход особенно полезен для функциональности, которая по сути своей является временной. Пример такой функциональности — специализированные страницы для освещения спортивных событий. Такие части сайта могут быть быстро собраны вместе с использованием быстрых языков программирования и удалены как только событие закончится. Мы видели похожий подход в финансовых системах, где новые сервисы добавлялись под открывшиеся рыночные возможности и удалялись через несколько месяцев или даже недель после создания.
Такой упор на заменяемости — частный случай более общего принципа модульного дизайна, который заключается в том, что модульность определяется скоростью изменения функционала. Вещи, которые изменяются вместе, должны храниться в одном модуле. Части системы, изменяемые редко, не должны находиться вместе с быстроэволюционирующими сервисами. Если вы регулярно меняете два сервиса вместе, задумайтесь над тем, что возможно их следует объединить.
Помещение компонент в сервисы добавляет возможность более точного (granular) планирования релиза. С монолитом любые изменения требуют пересборки и развертывания всего приложения. С микросервисами вам нужно развернуть (redeploy) только те сервисы, что изменились. Это позволяет упростить и ускорить процесс релиза. Недостаток такого подхода в том, что вам приходится волноваться насчет того, что изменения в одном сервисе сломают сервисы, обращающиеся к нему. Традиционный подход к интеграции заключается в том, чтобы решать такие проблемы путем версионности, но микросервисы предпочитают использовать версионность только в случае крайней необходимости. Мы можем избежать версионности путем проектирования сервисов так, чтобы они были настолько толерантны к изменениям соседних сервисов, насколько возможно.
За микросервисами будущее?
Наша основная цель при написании этой статьи заключалась в том, чтобы объяснить основные идеи и принципы микросервисной архиктуры. Мы считаем, что микросервисный стиль — важная идея, стоящая рассмотрения для enterprise приложений. Не так давно мы разработали несколько систем используя этот стиль и знаем несколько других команд, которые используют этот подход.
Известные нам пионеры этого архитектурного стиля — это такие компании как Amazon, Netflix, The Guardian, the UK Government Digital Service, realestate.com.au, Forward и comparethemarket.com. Конференции 2013 года были полны примеров команий, движущихся в направлении, которое можно классифицировать как микросервисы, например, Travis CI. К тому же, существует множество организаций, которые уже давно используют то, что мы называем микросервисами, но не используют это название. (Часто это называется SOA, хотя, как мы уже говорили, SOA может являться в самых разных и, зачастую, противоречивых формах.)
Несмотря на весь этот положительный опыт, мы не утверждаем, что микросервисы — это будущее проектирования ПО. И хотя наш опыт пока что весьма позитивен по сравнению с опытом использования монолитной архитектуры, мы подходим осознанно к тому факту, что прошло еще недостаточно времени для того, чтобы выносить такое суждение.
Часто настоящие последствия ваших архитектурных решений становятся видно только спустя несколько лет после того, как вы сделали их. Мы видели проекты, в которых хорошие команды с сильным стремлением к модульности разработали монолитные приложения, полностью «прогнившие» по прошествию нескольких лет. Многие считают, что такой результат менее вероятен в случае с микросервисами, т.к. границы между сервисами являются физическими и их сложно нарушить. Тем не менее, до тех пока мы не увидим достаточного количества проверенных временем систем, использующих этот подход, мы не можем с уверенностью утверждать, насколько микросервисная архитектура является зрелой.
Определенно существуют причины, по которым кто-то может считать микросервисную архитектуру недостаточно зрелой. Успех любых попыток построить компонентную систему зависит от того, насколько хорошо компоненты подходят приложению. Сложно понять где именно должны лежать границы компонентов. Эволюционный дизайн осознает сложности проведения правильных границ и важность легкого их изменения. Когда ваши компоненты являются сервисами, общающимися между собой удаленно, проводить рефакторинг намного сложнее, чем в случае с библиотеками, работающими в одном процессе. Перемещение кода между границами сервисов, изменение интерфейсов должны быть скоординированы между разными командами. Необходимо добавлять слои для поддержки обратной совместимости. Все это также усложняет процесс тестирования.
Еще одна проблема состоит в том, что если компоненты не подобраны достаточно чисто, происходит перенос сложности из компонент на связи между компонентами. Создается ложное ощущение простоты отдельных компонент, в то время как вся сложность находится в местах, которые труднее контролировать.
Также существует фактор уровня команды. Новые техники как правило принимаются более сильными командами, но техники, которые являются более эффективными для более сильных команд, необязательно являются таковыми для менее сильных групп разработчиков. Мы видели множество случаев, когда слабые команды разрабатывали запутанные, неудачные архитектуры монолитных приложений, но пройдет время прежде чем мы увидим чем это закончится в случае с микросервисной архитектурой. Слабые команды всегда создают слабые системы, сложно сказать улучшат ли микросервисы эту ситуацию или ухудшат.
Один из разумных аргументов, которые мы слышали, состоит в том, что вам не следует начинать разработку с микросервисной архитектуры. Начните с монолита, сохраняйте его модульным и разбейте на микросервисы когда монолит станет проблемой. (И все же этот совет не является идеальным, т.к. хорошие интерфейсы для сообщения внутри процесса не являются таковыми в случае с межсервисным сообщением.)
Итого, мы пишем это с разумным оптимизмом. К этому моменту мы видели достаточно примеров микросервисного стиля чтобы осознавать, что он является стоящим путем развития. Нельзя сказать с уверенностью к чему это приведет, но одна из особенностей разработки ПО заключается в том, что нам приходится принимать решения на основе той, зачастую неполной, информации, к который мы имеет доступ в данный момент.
Ссылка на оригинал статьи: Microservices
Го в Go! Как команда PHP взялась писать микросервисы / Блог компании Lamoda / Хабр
Всем привет! Меня зовут Алексей Скоробогатый, я системный архитектор в Lamoda. В феврале 2019 года я выступал на Go Meetup еще на позиции тимлида команды Core. Сегодня хочу представить расшифровку своего доклада, который вы также можете посмотреть.
Наша команда называется Core неспроста: в зону ответственности входит все, что связано с заказами в e-commerce платформе. Команда образовалась из PHP-разработчиков и специалистов по нашему order processing, который на тот момент представлял собой единый монолит. Мы занимались и продолжаем заниматься декомпозицией его на микросервисы.
Оформление заказа в нашей системе состоит из связанных компонентов: есть блок доставки и корзина, блоки скидок и оплаты, — и в самом конце есть кнопка, которая отправляет заказ собираться на склад. Именно в этот момент начинается работа системы order processing, где все данные заказа будут провалидированы, а информация агрегирована.
Внутри всего этого — сложная многокритериальная логика. Блоки взаимодействуют между собой и влияют друг на друга. Непрерывные и постоянные изменения от бизнеса еще увеличивают сложность критериев. Кроме того, у нас есть разные платформы, через которые клиенты могут создавать заказы: сайт, приложения, колл-центр, В2В-платформа. А также жесткие критерии SLA/MTTI/MTTR (метрики регистрации и решения инцидента). Все это требует от сервиса высокой гибкости и устойчивости.
Архитектурное наследие
Как я уже говорил, на момент образования нашей команды система order processing представляла собой монолит – почти 100 тысяч строк кода, в которых описывалась непосредственно бизнес-логика. Основная часть была написана в 2011 году, с использованием классической многослойной MVC-архитектуры. В основе был РНР (фреймворк ZF1), который постепенно оброс адаптерами и symfony-компонентами для взаимодействия с различными сервисами. За время существования у системы было более 50 контрибьюторов, и хотя нам удалось сохранить единый стиль написания кода, это тоже наложило свои ограничения. Плюс ко всему возникло большое количество смешанных контекстов — по разным причинам в систему были имплементированы некоторые механизмы, не связанные непосредственно с обработкой заказов. Все это привело к тому, что на настоящий момент мы имеем MySQL базу данных размером более 1 терабайта.
Схематично изначальную архитектуру можно представить так:
Заказ, конечно, находился на каждом из слоев — но помимо заказа были и другие контексты. Мы начали с того, что определили bounded context именно заказа и назвали его Customer Order, так как помимо самого заказа, там есть те самые блоки, которые я упомянул в начале: доставка, оплата и прочее. Внутри монолита всем этим было сложно управлять: любые изменения влекли к увеличению зависимостей, код доставлялся на прод очень долго, всё время увеличивалась вероятность ошибок и отказа системы. А мы ведь говорим про создание заказа, основную метрику интернет-магазина — если заказы не создаются, то остальное уже не так важно. Отказ системы вызывает немедленное падение продаж.
Поэтому мы решили вынести контекст Customer Order из системы Order Processing в отдельный микросервис, который назвали Order Management.
Требования и инструментарий
После определения контекста, который решили вынести из монолита в первую очередь, мы сформировали требования к нашему будущему сервису:
- Производительность
- Консистентность данных
- Устойчивость
- Предсказуемость
- Прозрачность
- Инкрементальность изменений
Мы хотели, чтобы код был максимально понятным и легко редактируемым, чтобы следующие поколения разработчиков могли быстро внести требующиеся для бизнеса изменения.
В итоге мы пришли к определенной структуре, которую используем во всех новых микросервисах:
Bounded Context. Каждый новый микросервис, начиная с Order Management, мы создаем на основе бизнес-требований. Должны существовать конкретные объяснения, какую часть системы и почему требуется вынести в отдельный микросервис.
Существующая инфраструктура и инструментарий. Мы не первая команда в Lamoda, которая начала внедрять Go, до нас были первопроходцы — непосредственно Go-шная команда, которая подготовила инфраструктуру и инструментарий:
- Gogi (swagger) — генератор спецификации по swagger.
- Gonkey (testing) — для функциональных тестов.
- Мы используем Json-rpc и генерим обвязку client/server по swagger. Также все это деплоим в Kubernetes, собираем метрики в Prometheus, для трейсинга используем ELK/Jaeger – все это входит в обвязку, которую создает Gogi для каждого нового микросервиса по спецификации.
Примерно так выглядит наш новый микросервис Order Management:
На входе у нас есть данные, мы их агрегируем, валидируем, взаимодействуем со сторонними сервисами, принимаем решения и передаем результаты дальше в Order Processing — тот самый монолит, который большой, неустойчивый и требовательный к ресурсам. Это тоже нужно учитывать при построении микросервиса.
Сдвиг парадигмы
Выбрав Go, мы сразу получили несколько преимуществ:
- Статическая строгая типизация сразу отсекает определенный круг возможных багов.
- Concurrency модель хорошо ложится в наши задачи, так как нам надо ходить и одновременно опрашивать несколько сервисов.
- Композиция и интерфейсы помогают нам также в тестировании.
- “Простота” изучения — как раз здесь обнаружились не только очевидные плюсы, но и проблемы.
Язык Go ограничивает воображение разработчика. Это стало камнем преткновения для нашей команды, привыкшей к РНР, когда мы перешли к разработке на Go. Мы столкнулись с настоящим парадигменным сдвигом. Нам пришлось пройти через несколько стадий и понять некоторые вещи:
- В Go тяжело строить абстракции.
- Go, можно сказать, Object-based, но не Object-oriented язык, так как там нет прямого наследования и некоторых других вещей.
- Go способствует писать явно, а не скрывать объекты за абстракциями.
- Go имеет Pipelining. Это вдохновило нас на построение цепочек обработчиков для работы с данными.
В итоге мы пришли к пониманию, что Go – это процедурный язык программирования.
Data first
Я думал, как визуализировать проблему, с которой мы столкнулись, и наткнулся на эту картинку:
Здесь изображен “объектно-ориентированный” взгляд на мир, где мы строим абстракции и закрываем за ними объекты. Например, тут не просто дверь, а Indoor Session Initialiser. Не зрачок, а Visitor Monitor Interface — и так далее.
Мы отказались от такого подхода, и на первое место поставили сущности, не став их скрывать за абстракциями.
Рассуждая таким образом, мы поставили на первое место данные, и получили такой Pipelining в сервисе:
Изначально мы определяем модель данных, которые поступают в конвейер обработчиков. Данные являются изменяемыми, причем изменения могут происходить как последовательно, так и конкурентно (concurrency). С помощью этого мы выигрываем в скорости.
Назад в будущее
Неожиданно, разрабатывая микросервисы, мы пришли к модели программирования 70-х годов. После 70-х возникли большие enterprise-монолиты, где появилось объектно-ориентированное программирование, функциональное программирование – большие абстракции, которые позволяли удерживать код в этих монолитах. В микросервисах нам все это не нужно, и мы можем использовать отличную модель CSP (communicating sequential processes), идею которой выдвинул как раз в 70-х Чарльз Хор.
Также мы используем Sequence/Selection/Interation — парадигму структурного программирования, согласно которой весь код программы можно составить из соответствующих управляющих конструкций.
Ну и процедурное программирование, которое в 70-х годах было мейнстримом 🙂
Структура проекта
Как я уже говорил, на первое место мы поставили данные. Кроме того, построение проекта “от инфраструктуры” мы заменили на бизнес-ориентированное. Чтобы разработчик, заходя в код проекта, сразу видел, чем занимается сервис — это и есть та самая прозрачность, которую мы определили как одно из основных требований к структуре наших микросервисов.
В результате мы имеем плоскую архитектуру: небольшой слой API плюс модели данных. А вся логика (которая ограничена у нас контекстом бизнес требования от микросервиса), хранится в процессорах (обработчиках).
Мы стараемся не создавать новые отдельные микросервисы без однозначного запроса от бизнеса — так мы контролируем гранулярность всей системы. Если есть логика, которая близко связана с существующим микросервисом, но по сути относится к другому контексту — мы вначале заключаем ее в так называемых сервисах. И только при возникновении постоянной бизнес-потребности мы выносим ее в отдельный микросервис, к которому далее обращаемся при помощи rpc-вызова.
Чтобы контролировать гранулярность и не плодить микросервисы необдуманно, логику, которая не относится непосредственно к этому контексту, но близко связана с данным микросервисом, мы заключаем в слое services. А потом, если есть бизнес-потребность, мы выносим ее в отдельный микросервис — и далее с помощью rpc-вызова обращаемся к нему.
Таким образом, для внутреннего API в процессорах у сервиса взаимодействие никак не меняется.
Устойчивость
Мы решили не брать заранее какие-то сторонние библиотеки, так как данные, с которыми мы работаем, достаточно чувствительные. Поэтому мы немного повелосипедили 🙂 Например, сами реализовали некоторые классические механизмы — для Idempotency, Queue-worker, Fault Tolerance, Compensating transactions. Наш следующий шаг — постараться это переиспользовать. Завернуть в библиотеки, может быть side-car контейнеры в Pod’ах Kubernetes. Но уже сейчас мы можем эти паттерны применять.
Мы реализуем в своих системах паттерн, который называется graceful degradation: сервис должен продолжать работать, независимо от внешних вызовов, в которых мы агрегируем информацию. На примере создания заказа: если запрос попал в сервис, мы в любом случае заказ создадим. Даже если упадет соседний сервис, отвечающий за какую-то часть той информации, которую мы должны сагрегировать или провалидировать. Более того – мы не потеряем заказ, даже если мы не сможем в краткосрочном отказе процессинга заказа, куда мы должны передать. Это тоже один из критериев, по которым мы принимаем решение, выносить ли логику в отдельный сервис. Если сервис не может обеспечить свою работу при недоступности следующих сервисов в сети, то либо нужно его перепроектировать, либо подумать о том, стоит ли его вообще выносить из монолита.
Го в Go!
Когда приходишь писать бизнес-ориентированные продуктовые микросервисы из классической сервис-ориентированной архитектуры, в частности РНР, то сталкиваешься с некоторым парадигменным сдвигом. И его обязательно нужно пройти, иначе можно наступать на грабли бесконечно. Бизнес-ориентированная структура проекта позволяет нам не усложнять лишний раз код и контролировать гранулярность сервиса.
Одной из основных наших задач было повышение устойчивости сервиса. Конечно, Go не дает повышения устойчивости просто “из коробки”. Но, по моему ощущению, в экосистеме Go оказалось проще создать весь необходимый Reliability kit даже своими руками, не прибегая к сторонним библиотекам.
Другой важной задачей было увеличить гибкость системы. И тут я однозначно могу сказать, что скорость внесения требуемых бизнесом изменений сильно выросла. Благодаря архитектуре новых микросервисов разработчик остается один на один с бизнес-фичей, ему не нужно думать о том, чтобы строить клиенты, слать мониторинги, пробрасывать трейсинги, и настраивать логирование. Мы оставляем для разработчика именно прослойку написания бизнес-логики, позволяя ему не задумываться обо всей инфраструктурной обвязке.
Собираемся ли мы полностью переписать все на Go и отказаться от РНР?
Нет, так как мы идем от бизнес-потребностей, и есть некоторые контексты, в которых РНР очень хорошо ложится — там не нужна такая скорость и весь Go-шный набор инструментов. Вся автоматизация операций по доставке заказов и управлению фотостудией сделана на PHP. Но, например, в e-commerce платформе в customer side мы почти все переписываем на Go, так как там это оправданно.
Событийные микросервисы с использованием RabbitMQ
Использование правильных шаблонов/паттернов для общения между микросервисами может помочь масштабировать ваше приложение и решить наиболее распространенные системные проблемы. Мы начали с прямых HTTP-вызовов для всех взаимодействий, но решили перейти к событийной системе. Эта система изменила наше мышление о взаимодействии между службами, навязала масштабируемые паттерны и увеличила отказоустойчивость.
Мы перешли к использованию событий вместо традиционного HTTP-взаимодействия по нескольким причинам.
Во-первых, это вынудило к разделеню сервисов. Из нашего опыта работы с HTTP, один сервис может делать вызовы к каждому сервису, который ему необходим, и это означало, что этому сервису нужна клиентская библиотека для каждого сервиса, к которому он обращается. Клиентская библиотека обеспечивала бы то, что ошибки не будут останавливать или блокировать функциональность и будут соответствовать каждой службе.
Так как мы разрослись до 20+ сервисов, разработка и поддержка клиентских библиотек стала долгим и трудоёмким процессом. Для новых сервисов, которые заменяли устаревшую функциональность, требовалось обновление всех зависимостей. Это сделало процесс разработки и выкладки более длительным и более подверженным ошибкам из-за множества задействованных частей.
Во-вторых, преимущество использования событий заключается в том, что сервисам больше не нужно организовывать функциональность, удаляя прямые вызовы к клиентам. Сервисы могут появляться и удаляться без надобности обновлять клиентские библиотеки или добавления нового HTTP-вызова. Мы можем быстро развернуть прототип приложения, которое слушает события, не беспокоясь о том, что оно приведёт к падению всей системы.
В-третьих, это изменение позволило нам реализовать глобальные паттерны. Мы добавили ограничения скорости и таймауты для каждого воркера, без необходимости их реализации в каждой из наших различных клиентских библиотек (GitHub, AWS, внутренних сервисах и т. д.). Мы также можем с легкостью реализовать паттерн Автоматический выключатель, отрезав слушателя события, пока он не восстановит свою работу. Нужно изменить только сам воркер, а не все приложения/сервисы, которые вызывают его.
И, наконец, нам не нужно держать открытым http-соединение для длительных обработок (которое может быть потеряно или ограничено в связи с большим количеством открытых сокетов и т. д.).
События и задачи
Есть два различных паттерна, которые составляют нашу систему, управляемую событиями: события и задачи.
События — это уведомления, которые сообщают подписанным приложениям, что что-то произошло. Приложения подписываются на определенные события и реагируют на них, создавая задачи для самих себя. События никогда не должны изменять состояние напрямую.
Задачи — это действия, которые изменяют состояние. Единственное, что может создать задачу для данного приложения — само приложение. Таким образом, приложения не могут напрямую изменить состояние друг друга.
Строгие соглашения по именованию помогают нам поддерживать согласованность и четкость, когда дело доходит до именования события и задачи. Задачи начинаем с названия приложения, чтобы быть уверенными, что они обрабатываются только этим приложением. Далее идет модель, чье состояние должно быть изменено задачей, и заканчивается описательным глаголом настоящего времени. Пример задачи будет api.user.authorize
. Опираясь на соглашение, мы знаем, что эта задача обрабатывается сервисом API
, и он хочет выполнить авторизацию
на объекте пользователь
.
События не имеют имени приложения, потому что на них могут быть подписаны несколько приложений. Они начинаются с имени модели, а заканчиваются глаголом прошедшего времени, который описывает то, что произошло. Примером события будет user.authorized
.
Наше приложение, разбитое на задачи и события, заставило нас изменить способ мышления. Раньше, если мы хотели отправить электронное письмо после того, как мы получили платеж, мы бы просто добавили вызов SendGrid в наш платежный сервис. Просто и понятно.
Но теперь с нашей новой событийной системой, наш платежный сервис генерирует событие org.payment.processed
. Наш сервис электронной почты, Pheidi, забирает это событие и создает задачу pheidi.email.send
. Теперь мы должны думать в терминах реакций вместо команд. Если нужны дополнительные данные, которые не были предоставлены в событии (зарегистрированное имя на кредитной карте, например), мы все еще используем http-вызовы в нашу биллинговую службу.
Однако, есть и некоторые минусы, которые появляются вместе с плюсами событийно-ориентированного подхода. Поскольку вы явно не вызываете сервис, вы не можете знать наверняка какой именно будет ответ на выброшенное событие. Это затрудняет отладку, потому что система сложнее и труднее для понимания.
Реализация
Мы используем RabbitMQ в качестве системы обмена сообщениями. Он отвечает за доставку событий до сервисов, которые слушают их. Задачи также проходят через RabbitMQ, таким образом балансируя нагрузку между несколькими экземплярами приложения. Мы выбрали RabbitMQ, потому что он был прост в установке и разворачивании и имеет много клиентский библиотек, готовых к использованию.
Мы создали единый шаблон воркера для взаимодействия с RabbitMQ. Вот некоторые паттерны, которые мы используем для обработки наших очередей.
Экспоненциальная отмена
В самом начале мы добавили экспоненциальную отмену для задач. Если задание выбросило ошибку, которая допускает повторную попытку, оно будет повторно запущено после задержки. Каждое задание начинает с минимальной задержкой по времени, которая удваивается, пока не достигнет заданного максимального предела (или до бесконечности, если предел не определен).
Изначально, мы хотели, чтобы количество повторных попыток выполнить задание было бесконечным, думая, что если что-то «зависнет», то наши системы оповещения будут в агонии и кто-нибудь из нас пойдёт спасать возникшую ситуацию. Поначалу это работало хорошо, но как только мы добавили больше заданий, количество «застрявших» в очереди выросло по разным причинам.
Максимальный предел повторов и Функция восстановления
Для борьбы с растущими очередями, мы добавили максимальное количество повторов для каждой очереди. Если задание повторяется заданное число раз, мы прекращаем его попытки и запускаем функцию восстановления. Функция восстановления логирует ошибку в базе данных. Теперь наши системы оповещения срабатывают на функции восстановления, что позволит нам заняться исправлением проблемы, вместо поднятия нашей очереди. Мы обнаружили, что лучше потерпеть неудачу быстро и показать ошибку пользователям, вместо того, чтобы заставлять их ждать долгое время, чтобы что-то произошло.
Предвыборки (Prefetch)
Prefetch — это важный параметр, который выставляется на канал RabbitMQ. Без этого ваш воркер будет принимать все доступные задания в очереди. Например, если ваше приложение испытало всплеск нагрузки и в очередь упало 10 000 заданий, все 10 000 заданий будут направлены воркеру и станут храниться в памяти, что, как правило, приводит к краху. Prefetch ограничивает количество заданий, которое ваш воркер будет держать в памяти. Этот пост в блоге RabbitMQ помог нам определить лучший способ задать prefetch.
Обмены и очереди
Для реализации событий и задач, мы используем следующие конструкции в RabbitMQ. Для задач используем одну очередь и API sendToQueue
. Поскольку задачи могут быть использованы только для одного приложения, мы не создаем для них обмен. Для событий немного сложнее. Приложение, выбрасывающее событие, создает веер обменов и каждый подписчик будет создавать и привязывать очередь к этому обмену. Это позволяет любому приложению получить любое событие, не затрагивая другие приложения.
Идентификаторы Транзакций
Одна вещь, которая помогла нам отладить и обеспечить самонаблюдение нашей системы событий — идентификаторы транзакции (TID). Каждое задание, которое мы посылаем в RabbitMQ, начинается с TID. Если это задание было результатом события или задачи, то он использует тот же TID. Если задание не создаётся из события или задачи, мы создаём новый TID. Это помогает нам отслеживать, какие события запускают какие задачи.
Итог
Наша событийно-управляемая система ускорила нашу разработку, сделала нас более устойчивыми к сбоям и улучшила отзывчивость нашего продукта для наших пользователей. Мы надеемся, что эти техники помогут вырасти и вашему продукту.
Просто о микросервисах / Блог компании Райффайзенбанк / Хабр
Вступление
Чуть ли не каждый второй, кто впервые сталкивается с MSA (Micro Service Architecture), на первых порах восклицает: «Да я эти микросервисы еще …надцать лет назад». Отчасти они правы. И я тоже был из этой самой половины, и не понимал — почему такой шум?
В самом деле! Ведь MSA — это тоже про разработку софта. Какие здесь могут быть революции? Все методики знакомы. В некоторых местах можно даже удивиться: «А разве бывает по-другому»? Фанаты Agile и DevOps тоже скажут, что это всё наше, родное.
Но всё же прошу вас набраться терпения и продолжить читать дальше.
Что такое микросервисная архитектура (MSA)
Если перефразировать Википедию, то определение микросервисной архитектуре может быть таким:
MSA — принципиальная организация распределенной системы на основе микросервисов и их взаимодействия друг с другом и со средой по сети, а также принципов, направляющих проектирование архитектуры, её создание и эволюцию.
Что такое микросервис (MS)
С архитектурой разобрались быстро. С микросервисами давайте поподробнее.
Понять суть микросервиса проще всего на сравнении, или даже противопоставлении его крупному приложению — монолиту. В отличии от MSA, я не буду давать определение микросервису, а перечислю его наиболее важные характеристики.
А дальше мы рассмотрим каждую из них подробнее.
Я выделил восемь свойств микросервиса:
- Он небольшой.
- Он независимый.
- Он строится вокруг бизнес-потребности и использует ограниченный контекст (Bounded Context).
- Он взаимодействует с другими микросервисами по сети на основе паттерна Smart endpoints and dumb pipes.
- Его распределенная суть обязывает использовать подход Design for failure.
- Централизация ограничена сверху на минимуме.
- Процессы его разработки и поддержки требуют автоматизации.
- Его развитие итерационное.
Уже на этом месте те, которые якобы создавали микросервисы в доисторические времена, должны задуматься, действительно ли всё было так продвинуто… Лично я на этой стадии перешел в разряд сомневающихся.
Небольшой
Что такое «небольшой»? Такая малоинформативная формулировка! На самом деле, по-другому не скажешь. Каждый должен самостоятельно определиться с размером. Лучше всего на практике. В качестве индикативных оценок можно ориентироваться на рекомендации экспертов. Размер микровервиса должен быть таким, чтобы выполнялось одно из условий:
- Один сервис может развивать одна команда не более чем из дюжины человек.
- Команда из полудюжины человек может развивать полдюжины сервисов.
- Контекст (не только бизнеса, но и разработки) одного сервиса помещается в голове одного человека.
- Один сервис может быть полностью переписан одной командой за одну Agile-итерацию.
Независимый
Микросервисная архитектура — воплощение паттернов High Cohesion и Low Coupling. Всё, что противоречит этому, отвергается беспощадно. В противном случае команду ждут большие проблемы. Так что микросервис обязан быть независимым компонентом.
Здесь попрошу вас не начинать холивар о том, что же такое «компонент». Давайте в рамках этой статьи сойдемся на том, что
Компонент — это единица ПО, код которой может быть независимо заменен или обновлен.
Конечно любая мало-мальски серьезная программа пишется с разбиением на компоненты, которые, безусловно, основываются на тех же принципах. Но в монолите общая кодовая база открывает возможности для нарушения низкой связанности. И при слабой дисциплине рано или поздно код превращается в спагетти.
Под такую формулировку компонента подходят и сторонние библиотеки. Здесь сложнее с нарушением границ произвольными связями, но не на много.
В то же время методология разбиения на отдельные микросервисы вынуждает придерживаться жесткого их разделения, ведь они должны отвечать более жестким критериям независимости.
Так, каждый микросервис работает в своем процессе и поэтому должен явно обозначить свой API. Учитывая, что другие компоненты могут использовать только этот API, и к тому же он удаленный, минимизация связей становится жизненно важной.
Такое разделение дает явный выигрыш с точки зрения независимого развития разных компонентов. И с учетом этого различные языки вводят конструкции, позволяющие явное создание независимых компонентов (например, модули в Java 9), и это перестает быть прерогативой микросервисного подхода.
Не хочу, чтобы создалось впечатление, будто в микросервисной архитектуре запрещено использование библиотек. Их использование не приветствуется, поскольку так или иначе приводит к зависимостям между микросервисами, но всё же допускается. Как правило, это допущение распространяется на инфраструктурные функции вроде логирования, вызова удаленного API, обработки ошибок и тому подобного.
Независимость микросервисов позволяет организовать независимый жизненный цикл разработки, создавать отдельные сборки, тестировать и развертывать.
Поскольку размер микросервисов невелик, то очевидно, что в крупных системах их будет немало. Управлять ими вручную будет сложно. Поэтому команда обязана иметь приемлемый уровень автоматизации согласно Continuous integration и Continuous Delivery.
Где же микросервис (бизнес-потребность)
Итак, вы решили спроектировать новый микросервис.
Определение его границ — самый важный шаг. От этого будет зависеть вся дальнейшая жизнь микросервиса, и это серьёзно повлияет на жизнь команды, отвечающей за него.
Основной принцип определения зоны ответственности микросервиса — сформировать её вокруг некоторой бизнес-потребности. И чем она компактнее, чем формализованней её взаимоотношения с другими областями, тем проще создать новый микросервис. В общем, довольно стандартный посыл. На нем основывается создание любых других компонентов. Вопрос только в том, чтобы в дальнейшем выдержать эту зону ответственности, что мы и обсуждали в предыдущем параграфе.
Когда границы микросервиса заданы и он выделен в отдельную кодовую базу, защитить эти границы от постороннего влияния не составляет труда. Далее внутри микросервиса создают свой микромир, опираясь на паттерн «ограниченного контекста». В микросервисе для любого объекта, для любого действия может быть своя интерпретация, отличная от других контекстов.
Но что делать, если границы оказались неправильными? В этом случае изменение функциональности в новом микросервисе ведет к изменению функциональности в других микросервисах. В результате «поплывут» интерфейсы всех зависимых микросервисов, а за ними интеграционные тесты. И всё превращается в снежный ком. А если эти микросервисы ещё и принадлежат разным командам, то начинаются межкомандные встречи, согласования и тому подобное. Так что правильные границы микросервиса — это основа здоровой микросервисной архитектуры.
Чтобы минимизировать ошибки при определении границ, нужно вначале их продумать. Поэтому оправданным является подход Monolith First, когда вначале систему развивают в традиционной парадигме, а когда появляются устоявшиеся области, их выделяют в микросервисы. Но всё течет и меняется. И границы тоже могут меняться. Главное, чтобы выигрыш от разбиения превышал сложности пересмотра этих границ. Такой подход к постепенному формированию набора микросервисов похож на итерационное развитие, используемое в Agile, ещё его называют «эволюционным проектированием» (Evolutionary Design).
Есть ещё одно интересное следствие создания микросервисов, соответствующее закону Конвея (Conwey Law).
Если организация использует монолитное приложение, то оно нарушает соответствие структуре и коммуникациям внутри организации. А команды разработчиков строятся вокруг архитектурных слоев монолита: UI, серверная логика, база данных.
Микросервисная архитектура приводит IT и бизнес в гармонию, с точки зрения Конвея. Поскольку микросервисы формируются вокруг бизнес-потребностей конкретных бизнес-подразделений, то архитектура предприятия начинает повторять оргструктуру и каналы социальной и бизнес-коммуникации. А команды становятся кроссфункциональными и формируются вокруг этих бизнес-потребностей / бизнес-подразделений.
Поскольку разные микросервисы получаются независимыми не только логически, но и технологически, а создавать их могут разные команды, ничто не мешает для каждого случая подбирать подходящие языки программирования, фреймворки и даже операционные системы.
Интеграция. Smart endpoints and dumb pipes
Интеграция микросервисов обходится без ESB, как центрального промежуточного звена. Наверное, комьюнити уже натерпелось от неудачных вариантов реализации этого подхода. То, что были и удачные — не принимается в расчет. Впрочем, ESB ещё и противоречит таким критериям как децентрализация и независимость. Таким образом, сложность интеграции распределяется с центрального звена в виде ESB непосредственно на интегрируемые компоненты: «умные конечные точки».
Для интеграции, как правило, используются простые текстовые протоколы, основанные на HTTP, чтобы нивелировать возможную технологическую разность микросервисов. REST-подобные протоколы являются практически стандартом. Как исключение, могут использоваться бинарные протоколы типа Java RMI или .NET Remoting.
Здесь есть дилемма. Конечно бинарные протоколы гораздо эффективнее. Но, во-первых, появляются технологические ограничения. Во-вторых, на бинарных протоколах сложнее реализовывать шаблон Tolerant Reader, сохраняя эффективность. В-третьих, опять появляется зависимость провайдера и потребителей, поскольку они оперируют одними и теми же объектами и методами, то есть связаны по кодовой базе.
Другая отличительная черта взаимодействия микросервисов — синхронные вызовы не приветствуются. Рекомендуется использовать один синхронный вызов на один запрос пользователя, или вообще отказаться от синхронных вызовов.
И еще пара замечаний.
- Основной сложностью разбиения монолита на микросервисы является не определение их границ. Они уже должны сформироваться и устояться. Сложность заключается в том, что локальные вызовы становятся удаленными. А это влияет не только на организацию вызовов, но и на стиль взаимодействия, так как частые вызовы уже не подходят. Скорее всего, надо пересматривать сам API, делать его более крупным, а, как следствие, пересматривать логику работы компонентов.
- Поскольку асинхронное событийное взаимодействие — практически стандарт в микросервисной архитектуре, то надо разбираться в создании событийной архитектуры (Event Driven Architecture), а сами микросервисы должны соответствовать требованиям Reactive.
Design for failure для распределенной системы
Одно из наиболее критичных мест в микросервисной архитектуре — необходимость разрабатывать код для распределенной системы, составные элементы которой взаимодействуют через сеть.
А сеть ненадежна по своей природе. Сеть может просто отказать, может работать плохо, может вдруг перестать пропускать какой-то тип сообщений, потому что изменились настройки файрвола. Десятки причин и видов недоступности.
Поэтому микросервисы могут вдруг перестать отвечать, могут начать отвечать медленнее, чем обычно. И каждый удаленный вызов должен это учитывать. Должен правильно обрабатывать разные варианты отказа, уметь ждать, уметь возвращаться к нормальной работе при восстановлении контрагента.
Дополнительный уровень сложности привносит событийная архитектура. А отладку такой системы — не одного микросервиса, а системы, где много потоков разнонаправленных неупорядоченных событий — даже трудно представить. И даже если каждый из микросервисов будет безупречен с точки зрения бизнес-логики, этого мало. По аналогии со спортом, «звёзды» не гарантируют звездную команду, ведь в команде важнее не «звезды», а слаженность всех её игроков.
И поскольку сложность таких систем очень высока, то проблему решают так.
- Не доводят систему до состояния «без сучка без задоринки». Это очень дорого. Конечно, это не значит, что система валится от первого дуновения. Она просто отвечает необходимым нефункциональным требованиям. Но в ней могут присутствовать ошибки, незначительно влияющие на ее устойчивость и производительность.
- С другой стороны вкладываются в инфраструктуру, которая помогает быстрее устранять нештатные ситуации. Должно быть полное покрытие кода unit тестами, интеграционными и тестами производительности. Должен быть интеллектуальный мониторинг, который не только моментально показывает неработающие места, но и сигнализирует об ухудшении состояния системы с прогнозированием возможных сбоев. Должно быть продвинутое распределенное логирование, позволяющее оперативно проводить расследования. И часто по результатам исправляются скрытые ошибки.
Всё это полезно и любому монолиту, но для микросервисов такая инфраструктура — вопрос жизни и смерти.
Децентрализация данных
Еще один из важнейших элементов в парадигме микросервисов.
Каждому микросервису по своей базе данных!
Лозунг популиста на выборах.
На самом деле и в монолите можно побороться за изолированность компонентов, например, на уровне серверного кода. Если время от времени изоляция даёт течь, современные инструменты предлагают продвинутые инструменты рефакторинга. Пользуйтесь. Хотя, как правило, на это находится время, только когда дела уже совсем плохи.
Теперь опустимся ниже, на уровень базы данных. Почему-то здесь на изолированность обращают внимание гораздо реже. В результате через пару тройку лет активного развития в базе данных монолита образуется если не хаос, то энтропия продвинутого уровня. Чтобы её побороть, мало уже одной строчки в бэклоге. Необходимы месяцы кропотливого и долгого труда.
В микросервисной архитектуре это решается гильотиной. Общей базы данных просто нет.
Помимо изолированности есть и побочные плюсы. Например, легче реализовать Polyglot Persistence, когда база подбирается под конкретные цели. Ничто не мешает делать это и без микросервисов, и так часто делают. Но всё же в одном случае это закон, в другом — исключение.
У этой медали есть оборотная сторона. Много баз, много контекстов, как их все согласовать? Старая техника распределенных транзакций сложна и обладает низкой скоростью. Возможно это иногда можно пережить. А вот необходимость синхронного взаимодействия нескольких микросервисов не может устраивать, и это не побороть.
Проблема решается нетрадиционно для монолита: отказом от постоянной согласованности данных. Добро пожаловать в мир Eventual consistency. На первых порах это вызывает волну «справедливого» гнева. Но если разобраться, то нужна ли повсеместно немедленная согласованность данных по окончании транзакции? При детальном рассмотрении значительную часть случаев можно отбросить. Где возможно, заменяют одну распределённую транзакцию серией локальных с компенсационными механизмами. Где-то мирятся с временной несогласованностью. А возможные ошибки либо обрабатывают за счет более сложной архитектуры, либо благодаря данным мониторинга. Если ничего не получается, то в особо экстремальных случаях всё же используют распределенные транзакции. Но это, с моей точки зрения, нарушение принципов MSA.
Монолит против микросервисов
Микросервисный подход несет довольно много проблем. Их найти не трудно и каждый может поупражняться.
Например, организационные вопросы. Как удержать в согласованном по версиям состоянии сотню микросервисов, которые еще и постоянно и непредсказуемо редеплоятся. А доступ к средам у каждого инженера каждой команды? Какая команда напишет интеграционные тесты? И если кто-то согласится, то попробуй еще их напиши для такой запутанной конфигурации. А если возникает ошибка, то чья она? Только той команды, у которой сломалось? Как не узнать вечером в пятницу, что версия API N-го сервиса, которой вы пользуетесь, вдруг стала deprecated?
Да, это действительно проблемы. Но команды, которые практикуют Agile и DevOps, уже знают решение. Поэтому начинать путь к микросервисной архитектуре стоит с внедрения этих практик.
Кроме организационных есть и чисто архитектурные. Как перейти от монолита, где всё синхронно, согласованно и едино, к распределенной событийной архитектуры, основанной на множестве мелких элементов, в которой надо учитывать возможную неконсистентность данных? Одного этого достаточно, чтобы задуматься: а стоит ли игра свеч? На этом фоне, например, падение скорости обработки одного запроса кажется мелочью. Хотя бы работает!
Тогда зачем? Если у вас нет проблем с вашим «монолитом», то не надо их искать.
Но если проблемы есть, то посмотрите на плюсы MSA, и возможно она спасет вас.
Разбиение на независимые компоненты даёт безусловные и неоспоримые преимущества: легкое понимание контекста, гибкость развития, управления и масштабирования. Независимость и небольшой размер дают и неожиданные плюсы с точки зрения инфраструктуры. Вам теперь не нужна монструозная машина за 100500 долларов. Микросервисы можно устанавливать на обычные дешевые машинки. И окажется, что даже все вместе они будут стоить на порядок меньше, но работать эффективнее той самой супермашины, на которую у вас в организации, наверняка, молятся и сдувают с неё пылинки.
Здесь уместен другой лозунг от популиста. Хотя, как и предыдущий, он вполне серьезен.
Каждому микросервису по своему серверу!
Продолжим агитировать за микросервисы. Посмотрим на лидеров IT-индустрии: Amazon, Netflix, Google и другие показывают впечатляющие результаты. Их гибкость и скорость вывода новых продуктов поражают. Поэтому игра точно стоит свеч! Здесь уместно вспомнить, что в упомянутых организациях команд «уровня бог» не одна и не две. Им сложности микросервисной архитектуры вполне по зубам. И если предложить создать монолит, то они и его сделают так, что он будет сверкать путеводной звездой.
А, например, Amazon вполне себе работал на монолите, уже будучи гигантом и имея миллиардные обороты. Сайт газеты Guardian до сих пор, а возможно и навсегда, базируется на микросервисах вокруг монолита. Это говорит о том, что значительная часть задач успешно, а зачастую и легче, решается без привлечения микросервисов.
И всё же это не значит, что микросервисы не для вас. Не боги горшки обжигают. Но бросаться с головой в омут тоже не стоит. Для микросервисной архитектуры команда должна быть достаточно зрелой. Один из главных критериев: использует ли она Agile и DevOps? Команда должна быть грамотной. Это сложно формализовать, но всё же попробуйте трезво оценить возможности. Например, насколько команда продвинута в Reactive и Event-Driven Architecture? К тому же команда должна иметь подготовленную инфраструктуру для поддержки микросервисной системы.
Впрочем, достаточно. Просто попробуйте. Надеюсь, получится и понравится.
По стопам лучших: микросервисная архитектура в разрезе
Виды архитектуры
Простейший и популярный вариант архитектуры – монолитная. Каждый начинал с неё, и здесь нет никакой изоляции и распределённости: один монолит обрабатывает все запросы.
Отчего возникают следующие проблемы:
- отказоустойчивость;
- горизонтальное масштабирование;
- применение одной технологии или языка и невыгодность переписывать огромный монолит;
- сложность рефакторинга из-за хранения кода в одном месте и куча legacy;
- трудности работы в команде разработчиков;
- чтобы использовать повторно, придётся дробить.
Второй по популярности вид архитектуры – пара монолитов, микс из монолита и сервисов или даже микросервисов. То есть вы сохраняете монолит, а доработки выполняете с использованием современных технологий.
Это частично решает проблемы отказоустойчивости, масштабируемости и одного стека технологий.
Микросервисная архитектура – не новая идея, а разновидность сервис-ориентированной архитектуры. Сервис-ориентированная архитектура предусматривает модульность разработки и слабую связанность компонентов, поэтому получаем изолированную и распределённую систему.
Главный минус – общая шина данных Enterprise Service Bus с огромными спецификациями и сложностями работы с абстракциями и фасадами.
Микросервисная архитектура
Микросервисная архитектура наследует от предшественницы изоляцию и распределённость. Здесь база данных не используется как шина данных, за исключением отдельных случаев в пользу производительности. По классической схеме компоненты изолируются и на уровне кода, и на уровне базы.
Для микросервисов применяют контейнеризацию с оркестрацией и другими плюшками.
Следующее преимущество – протоколы обнаружения сервисов. Оцените наглядно разницу коммуникаций сервис-ориентированной и микросервисной архитектуры: у последней нет общей шины, и сервис обращается к любому другому напрямую:
Выбор протоколов общения зависит от программиста. Например, вы используете REST для публичных запросов и RPC через AMQP для внутренних либо один общий протокол для всех.
Разделяют микросервисы с точки зрения либо бизнеса, либо программиста для переиспользования. Но мешают этому две вещи:
- внутренние связи – при тесном взаимодействии микросервисы объединяют;
- транзакции – у разных микросервисов базы данных изолированы, а нужна одна общая.
Рассмотрим пример разделения сервисов. Вначале поставили задачу прикрутить конфигурацию к авторизации, позже потребовалась аналогичная для кошелька. Само собой, напрашивается выделение конфигурации в отдельный микросервис. Для этого копируют код из предыдущей задачи, добавляют RPC для внутренних связей, абстракцию над клиентом для удобства и начинают использовать в других микросервисах.
Достоинства и недостатки микросервисной архитектуры
Как в любой распределённой архитектуре, получим накладные расходы на коммуникацию.
Концепция непрерывной интеграции и доставки (CI/CD) и построение архитектуры (контейнеризация, оркестрация, мониторинг и другое) требует большого количества времени.
Что насчёт отказоустойчивости? Часто её определяют как «падение одного сервиса не отражается других». Представьте, падает Audit
, а Wallet
теоретически продолжает работать – похоже на отказоустойчивость:
А как же запросы по RPC, которые Wallet
продолжает слать? Необходимо программно предусмотреть ситуацию, когда Audit
не отвечает, и грамотно настроить rollback, поскольку базы разные, и транзакционно это сделать не получится.
Или другая ситуация: падает микросервис авторизации, через который ходят другие. Чтобы продолжать обрабатывать запросы, добавляют код для неавторизованного пользователя. По существу, это мощный отказ.
Масштабируемость у микросервисов достигается благодаря системам оркестрации, ведь остаётся достаточно серверных ресурсов в противоположность монолиту, который потреблял всю память и процессор.
Стандартный процесс разработки – кодинг, тестирование и развёртывание – в микросервисной архитектуре выглядит иначе. Первые два этапа сливаются, поскольку микросервис взаимодействует с кучей других. Чтобы локально сделать хоть один запрос, придётся запустить все эти микросервисы, поэтому тестирование вручную не подходит для подобной задачи.
Представьте, вы тестируете перевод средств в банковской системе. Для этого вы создаёте двух пользователей, компанию, агента для пополнения счёта, делаете эмиссию в систему, чтобы пополнить хранилище агента деньгами, вносите средства на личный счёт отправителя и только потом делаете перевод получателю.
Будете выполнять столько запросов вручную? Добавьте ещё сложности взаимодействия микросервисов и получите ад.
Поэтому локально разработчик проводит юнит-тестирование, где вместо ответов микросервисов будут mock-объекты. Ещё понадобятся функциональные тесты, например, для отлавливания проблем коммуникации, а также интеграционные тесты. Они прогоняются вместе с юнит-тестами на этапе слияния рабочий копий в главную ветку разработки. И только потом программист проверяет функциональность руками. До развёртывания релизную версию тестирует QA.
Микросервисная архитектура делает компоненты независимыми при разработке и развёртывании, чего не было в монолите.
Микросервисы используются повторно и экономят средства в плане бизнеса.
Программист получает относительную свободу в выборе языка и технологий для разработки отдельных частей проекта.
Контроль зависимостей
Трудно сопроводить и поддерживать 50 проектов с 50 репозиториями, если вдруг обнаружится баг безопасности, который нужно срочно пофиксить во всех них. Поэтому используйте контроль зависимостей, общие библиотеки для микросервисов и семантическое версионирование. Причём не делайте одну раздутую библиотеку, а разбивайте её на кучу маленьких, чтобы избежать страха выпуска мажорной версии.
Инвертируйте зависимости: прописывайте версию драйвера в пакет, куда добавляете нужные микросервисы, чтобы потом не пришлось сломя голову перепроверять версии во всех проектах. Для внешних зависимостей лучше зафиксировать версии.
Базы данных
Поскольку базы данных в микросервисной архитектуре изолированные, вы используете разные их виды одновременно и получаете повышенный уровень безопасности, благодаря общению сервисов только через RPC. Но что делать, когда объединяемые данные в разных микросервисах?
Вот возможные решения проблемы:
- храните одно и то же значение в двух микросервисах, но появляются трудности с актуализацией данных;
- делайте RPC, правда, усложните работу с большими объёмами информации;
- выгрузите данные из всех баз для аналитики;
- сделайте миграцию данных, что тоже непросто и повлечёт написание RPC.
Представьте другой случай: при регистрации создаётся 3 сущности (пользователь, профиль и счёт), но на полпути что-то падает. Как откатить изменения, если данные распределяются по разным микросервисам? Думайте об этом на этапе проектирования.
Внутренняя коммуникация в микросервисной архитектуре
Для общения микросервисам нужен контракт: протокол и валидация данных. С последним справляется JSON Schema, но протокол также необходим. Требования при выборе способа коммуникации:
- строгость;
- общий протокол для сервера и клиента;
- генерация кода для любого языка;
- производительность.
В качестве протоколов используют Protocol Buffers, FlatBuffers, Apache Thrift. Сначала вы пишете предметно-ориентированный язык, отдаёте это программе-генератору кода и получаете сгенерированный клиент и сервер.
Организация работы в команде
Команды делят по технологиям и следят за их размерами (не более 7–8 человек). Как правило, задачи выдают сеньорам, рядом с которыми хватает разработчиков уровнем ниже.
К примеру, два сеньора из одной команды получили по два микросервиса. Важно, чтобы программисты взаимодействовали между собой не только в локальной группе по конкретной задаче, но и с другими. Тогда в области их знаний будет много общего:
- язык;
- организация и шаблонизация кода для каждого микросервиса;
- библиотеки;
- концепция непрерывной интеграции и доставки;
- протокол коммуникации;
- документация.
А что объединит разнородные по языкам команды? Останется только CI/CD, глобально стандартизованный прокол и подход к документированию.
Устройство микросервисов
Микросервисы состоят из трёх слоёв: небольших обработчиков, бизнес-логики и мапперов данных. Последовательность выполнения запроса выглядит так:
В сервисном слое сосредотачивается 99% всего кода. Поскольку в микросервисе несколько обработчиков, используйте Data Transfer Object (DTO), к которому вы будете приводить GET-запрос. Это облегчает обработку и валидацию.
Примеры кода
Не забывайте, что Node.js – асинхронный язык. Рассмотрим пример: приходит запрос, синхронно берёт HolyJSService
, асинхронно работает с маппером, выполняющим запись в базу данных, и процесс повторяется для последующих запросов. Никакого порядка в этом не наблюдается, поэтому выделяют два архитектурных подхода:
- stateless – не хранить состояние;
- stateful – добавлять объекту класса поля, которые нужны для выполнения запроса.
Когда используете stateful, при получении запроса вы создаёте новый объект HolyjSService
и заполняете необходимыми для выполнения данными. Дальше этот сервис асинхронно идёт в базу данных. Следующий запрос берёт новый экземпляр сервиса, и процесс повторяется.
В коде вы реализовываете абстрактный класс с кучей полей и сеттеров, наследуете от него новый сервис и наполняете методами.
Дальше обработчику нужен контейнер DI для получения нового экземпляра, который он пробрасывает в функцию. После её запуска и выполнения всех сеттеров вызываем сервисы в методе create
и возвращаем результат.
При stateless-подходе используется контекст, который создаётся для каждого запроса отдельно, и единственный экземпляр сервиса, поскольку выполнение запросов от него не зависит.
В контексте хранятся все необходимые поля и собственно метод для его создания:
На уровне сервиса выглядит как дополнительный параметр:
Для обработчика добавляем контекст вторым параметром – его возвращает метод createContextFromHttpRequest
:
Сами по себе хэндлеры предельно простые:
Поскольку для выполнения запросов часто нужны другие сервисы, контекст приходится прокидывать в них. Допустим, на первый микросервис поступил запрос. Присваивайте ему идентификатор X-Request-Id
, с которым он пройдётся по всем микросервисам. Благодаря логированию вы легко отследите путь внешнего запроса. X-Trace-Id
используется для обозначения единой бизнес-операции, состоящей из нескольких запросов.
Для создания идентификаторов используют OpenTracing.
Рассмотрим пример, когда не получается написать микросервис без сервиса. Вам дали задачу реализовать управление доступом на основе ролей (RBAC). Вы проектируете три сущности: роль, ресурс и правило. Пишете RPC, а из соображений производительности и удобства добавляете дополнительный сервис для работы с этим RPC, ролями, правилами и ресурсами.
Теперь отдельные микросервисы используют созданный сервис и общий интерфейс.
Если команде, которая пишет на другом языке, также понадобился RBAC, придётся написать для них дополнительный RPC. Однако не получится создать RPC для добавления фильтров или условий к управлению доступом.
Заблуждения
1. Это легче
После прочитанного вы вряд ли посчитаете микросервисную архитектуру лёгким делом, но это распространённое заблуждение. Сложность создания микросервиса наглядно показывает график:
С самого начала вы медленно и печально разбираетесь с документацией, бесконечно подбираете протоколы и другие компоненты. К энному микросервису вы нарабатываете стандартные шаблоны, что в несколько раз облегчает процесс.
С точки зрения поддержки – наоборот, потому что увеличивается количество микросервисов. Новый разработчик вынужден будет часами сидеть за изучением диаграмм, чтобы понять глобальную картину и принцип работы системы.
2. Лучше производительность
Представьте, сколько придётся ждать отправителю комплексного запроса, пока он сходит в кучу разных микросервисов. Поэтому ради производительности переезжать на эту архитектуру не всегда целесообразно. В качестве альтернативы перепишите куски проекта на более эффективном языке, или добавьте профилирование к монолиту.
Заключение
Принимайте решение об использовании микросервисной архитектуры, только чётко осознав и взвесив все достоинства и недостатки. Вам будут нужны знания о создании распределённой архитектуры и возможность реализации необходимых структурных элементов со стороны бизнеса. Ведь без логирования, мониторинга, трассировки, непрерывной интеграции и оркестрации вы обрекаете себя на ад.
А какой тип архитектуры используете вы?
Остановитесь!!! Вам не нужны микросервисы / Блог компании Southbridge / Хабр
Идет 2020 год. Если вам нужно пояснение, что такое микросервисы — лучше потратьте свое драгоценное время на что-то другое. Но если вы впечатлены историями успеха о микросервисах и хотите нырнуть в «панацею» с головой — продолжайте читать. Прошу прощения, будет немного длинновато (не очень, прим. переводчика).
Хотя мысль об этом витала уже некоторое время, необходимость ее записать возникла недавно, после захватывающего разговора с парой человек. Меня пригласили для того, чтобы достичь кворума при решении одной весьма интересной проблемы: «что такое микросервисы и должны ли мы следовать этой архитектуре для нашего ПО?».
По первой части вопроса договорились легко, а вот вторая встала весьма остро. Через пару минут общения часть фактов была собрана:
- Есть желание использовать микросервисную архитектуру в будущем продукте, надо это как-то утвердить.
- Кворум был в большей степени нетехническим, чем больше он переходил в «техническую плоскость», тем более неуместным он становился.
- Длинные паузы и отсутствие вопросов подразумевали незнание функционала как веб-сервисов, так и микросервисов.
Я их не осуждал за отсутствие знаний о функциях веб-сервисов или о следствиях работы микросервисов. Собеседники, конечно же, весьма превосходили меня в работе в своей отрасли. Но они хотели запрыгнуть в микросервисы без оценки возможных последствий!
Впервые о микросервисах я узнал еще в 2013 году, это был ролик на YouTube, где пояснялась архитектура сервисов Netflix. Было весьма впечатляюще, но я пропустил все без сожаления, поскольку это было слишком сложно для меня, человека, который тогда только начал постигать принципы разработки. Скоро это стало навязчивой идеей, поскольку на новом проекте объявили о внедрении микросервисов. Разработка проекта была увлекательной, он до сих пор остается лучшим по кодовой базе из тех, что попадали ко мне.
Честно говоря, широкие возможности модульной разработки были где-то далеко от меня, и я просто добавил дополнительный уровень сложности, будучи невежественным разработчиком, скрывавшимся от DevOps. Спустя пять лет я уже работаю с совершенно новым продуктом и другими людьми. У меня куча проблем, возникших из-за плохо разработанных микросервисов, приправленных тактикой девопсов-любителей (девляпсов?, прим. переводчика). Пелена довольно скоро спала, обнажив хрупкость микросервисов. Также я начал смотреть на всю эту архитектуру более опытным взглядом. Было поздно, но лучше поздно, чем никогда.
Видя, что микросервисы играют роль как главного героя, так и главного злодея, я сознательно убедил себя стать «адвокатом дьявола». А если вы архитектор или разработчик, выбирающий микросервисную архитектуру по умолчанию — придержите коней и задайте себе несколько вопросов.
Ваше приложение достаточно большое, чтобы дробить его на микросервисы?
Давайте признаем это. Не все приложения достаточно велики, чтобы их можно было разбить на более мелкие сервисы. Микросервисы, как следует из их названия, предоставляют набор небольших обособленных сервисов, каждый из которых выполняет некую роль. В идеальном мире ожидается, что каждый сервис будет сам по себе полноценным приложением.
На картинке выше представлено сравнение «стоимость строчки кода» между микросервисами и монолитным приложением. Микросервисы дороже, даже в простом случае, поскольку есть минимальная цена ресурсов, необходимых для работников и оборудования. Об этом должен думать каждый, а если вас это не заботит, возможно, вам вообще не стоит принимать такие решения.
Никто не спорит, что в будущем размер кодовой базы может вырасти, а это может добавить новый уровень сложности, но также не стоит забывать о том, что правильно разработанный код всегда можно перевести на микросервисы при приближении к пороговому значению.
Нужно ли на самом деле масштабировать отдельные компоненты приложения?
Предположим, что владелец продукта обратился к вам с идеей системы управления для автоматизации отдела кадров компании, в которой работает от 10.000 сотрудников. Технический энтузиаст внутри вас ответит мгновенно: микросервисная архитектура.
Конечно, это граничный случай, но общий смысл вы уже уловили! Одно из основных преимуществ использования этой архитектуры — простота масштабирования отдельного компонента. Можно найти множество приложений, в которых этот принцип сработает, но нужно ли это вашему приложению?
Есть ли транзакции, распределенные между сервисами?
Возможно, это наиболее сложное и стратегическое решение, ведь транзакции, охватывающие несколько сервисов, отвечают за всю архитектуру. Обработка транзакций в разных сервисах означает совместную блокировку между сервисами, часть таких взаимоблокировок трудноотслеживаемы и создают состояние гонки. Эти плавающие ошибки причиняют вред сервисам, а иногда портят кровь и инженерам.
Cервисы REST не имеют состояния по определению, а также не должны участвовать в транзакциях, которые проходят по нескольким сервисам. В мире высокой производительности двухфазный коммит (2PC) — ненужное зло. Шаблон SAGA добавляет еще один уровень сложности, к которому вы не будете готовы.
Микросервисы создают возможные проблемы целостности данных из-за того, что по своей сути настойчиво предлагают децентрализованное управление данными. В монолитном приложении можно обновить очень многие вещи в одной транзакции, а для обновления этих же данных в микросервисах требуется несколько заходов, в то время как распределенные транзакции не приветствуются (по уважительной причине). Поэтому разработчики должны знать о проблемах целостности, а также выяснять, как выявить отсутствие синхронизации, прежде чем писать код, о котором потом пожалеют. Martin Fowler
Возможно ли иметь распределенные по сервисам транзакции?
Да, конечно.
Стоит ли делать цепочку действий поверх сервисов без состояния (stateless)?
Возможно, не стоит!!
Есть ли нужда в частом общении между сервисами?
В обычном монолитном приложении каждый экземпляр микросервиса предоставляется в виде модуля системы. Связь между модулями работает в памяти с задержкой, близкой к нулю. Внедряя микросервисы не стоит забывать, что связь между сервисами перешла от транзакций в памяти к передаче по проводам.
Есть много проверенных и протестированных решений, но цена у них одна и та же — задержка. При переходе к микросервисам задержка вырастает от наносекунд до микросекунд. Попробуйте представить, что есть три сервиса, передающие данные по сети. Предполагая, что каждый вызов сервиса занимает 100мс (не очень быстро под нагрузкой), вы потратите 300мс только на сеть.
Следует учесть, что некоторые приложения тесно связаны с компонентами и сервисами по своему устройству. Дополнительные издержки связи могут привести к катастрофическим последствиям в приложениях, обрабатывающих данные в реальном времени. Попробуйте представить задержку связи в хирургическом костюме или в системе управления воздушным движением!
Кое-что ещё
- Добавленная сложность — конечно же, она не может быть определена в абсолютном значении, но ее можно сравнить относительно чего-либо. Несмотря на то, что микросервисы изначально разрабатывались для уменьшения сложности за счет разбиения приложения на более мелкие части — их архитектура более сложная в развертывании и обслуживании.
Следует помнить, что усредненная организация в IT не обладает такими же навыками, как команды инженеров Netflix. mike_pfeiffer
Цена распределенных систем. Микросервисы — распределенные системы с молекулярностью. Но распределенность имеет свою цену. Монолит был бы развернут на большой виртуалке или в некотором предварительно сделанном контейнере. Но микросервисы должны разворачиваться независимо (в идеальном мире) на нескольких виртуалках или контейнерах. Конечно же, они бывают небольшими по размеру, но это стоит также учитывать, и помнить, что я еще не начал говорить о затратах на управление и обслуживание.
Внедрение DevOps может быть как полезным, так и вредным. DevOps — проверенное и широко распространенное решение, но если вы работаете в маленькой компании, создание команды DevOps может принести больше вреда, чем пользы. Но одно можно сказать точно — нельзя поддерживать и управлять микросервисами без команды DevOps.
Сильная связность — некоторые приложения сильно связаны по своей природе. Разбиение на микросервисы, чтобы «вписаться» в архитектуру, будет катастрофическим.
Отсутствие опыта. Отсутствие опыта имеет решающее значение для любой проблемы, и оно не ограничивается только SOA. Но когда дело доходит до микросервисов, это может стать решающим фактором. Если вы разворачиваете приложения в неправильном порядке или произойдет сбой и зависимый сервис упадет — будет уже поздно.
Сквозное тестирование. Типичное монолитное приложение позволяет запускать тест практически мгновенно. Наличие нескольких служб с взаимозависимостью задержит тестирование без жизнеспособной оркестровки.
Хаотичные контракты на данные. Разработка и хранение контрактов на данные внутри команды сильно отличается от их распространения среди команд. А когда вы работаете с микросервисами, ваша команда может не находиться в одном регионе, не говоря уже об использовании одного и того же языка программирования. Выработка контрактов с данными для особых нужд будет стоить вам времени и места.
Устаревшая кодовая база — давайте будем честными. Для большинства из нас работа с устаревшей кодовой базой — это повседневная деятельность. Это хлеб с маслом большинства организаций. Быстро меняющиеся технологические достижения ставят нас впереди всех, но и в то же время они изолируют нас от устаревшей кодовой базы. Вы уверены, что только что разработанный фреймворк, использующий RabbitMQ, хорошо работает с устаревшим приложением, работающим на AIX?
Устранение неполадок — каждый сервис будет иметь свой собственный набор файлов журналов для анализа. Больше сервисов = больше файлов.
Заключение
Я здесь, чтобы сказать «Не используйте микросервисы»?
Конечно же, нет!!
Микросервисы заслужили известность. Они решили проблемы, которые считались неразрешимыми. История Netflix по применению микросервисов вдохновила многих. И список определенно не останавливается на Netflix. Uber, SoundCloud и гигантский Amazon — вот некоторые примеры, которые у всех на слуху. И не думайте, что истории успеха ограничиваются только потребительскими приложениями. Я имел непосредственный опыт работы с американским гигантом здравоохранения и был очарован возможностями дизайна, каждый раз, когда открывал исходный код.
Если бы вы доверились этой идее пять лет назад — я бы не осуждал вашу доверчивость. Время было другое, но теперь все, что можно сделать — быть честным. Сейчас 2020 год. Мы достаточно обожглись, и вокруг нас слишком много авгиевых конюшен. Внедряя бездумно архитектуру микросервисов, вы просто превращаете свой плохой код в плохую инфраструктуру.
Мне нравятся увлеченные программисты, я сам такой когда-то был таким, да и все еще есть. Они преданы тому, что делают, и переходят все границы, чтобы решить свою проблему. Но вы не можете так же энергично принимать решения, которые могут стоить вам и организации целое состояние, извините за резкость. Микросервисы не должны быть вашей архитектурой приложений по умолчанию. Это не серебряная пуля, которую вы искали. Сохраняйте равновесие с KISS и YAGNI.
Как сторонник технологии и энтузиаст, вы имеете право что-либо предпочитать. Однако то, что показывает вас с лучшей стороны — способность выбирать прагматично, когда варианты находятся между «правильным выбором» и «вашим любимым выбором».
PHP Микросервисы [Книга]
Описание книги
Переход от монолитных архитектур к высокодоступным, масштабируемым и отказоустойчивым микросервисам
Об этой книге
- Создавайте собственные приложения на основе микросервисов, управляемых событиями, и настраивайте их на рабочем сервере.
- Преобразуйте любое монолитное приложение в микросервис.
- Следите за состоянием вашего приложения, предотвращайте простои и сокращайте расходы.
Для кого эта книга
Разработчики PHP, которые хотят создавать масштабируемые, высокодоступные и безопасные приложения, найдут эту книгу полезной. Знание микросервисов не предполагается.
Что вы узнаете
- Настройте среду разработки, используя правильные стратегии и инструменты.
- Узнайте о дизайне и структуре приложения, чтобы приступить к реализации своего приложения.
- Преобразование монолитного приложения в микросервисы.
- Узнайте, как лучше всего начать реализацию вашего приложения с помощью тестирования.
- Узнайте, как отслеживать свои микросервисы, обрабатывать ошибки и отлаживать приложение.
- Разверните готовое приложение в производственной среде и узнайте, как решать типичные проблемы.
- Знайте, как масштабировать ваше приложение на основе микросервисов, когда оно будет запущено?
Подробно
Мир уходит от громоздких, ненадежных и требовательных к обслуживанию PHP-приложений к небольшим, простым в обслуживании и высокодоступным микросервисам, и разработчикам PHP настоятельно необходимо понимать важность в создании эффективных масштабируемых микросервисов.Эта книга станет надежным источником информации, которая поможет вам развить свои навыки и научит методам создания надежных микросервисов на PHP.
Книга начинается с введения в мир микросервисов и быстро показывает, как настроить среду разработки и построить базовую платформу с помощью Docker и Vagrant. Затем вы познакомитесь с различными аспектами дизайна, которые необходимо учитывать при создании микросервисов в вашей любимой платформе, и изучите такие темы, как тестирование, защита и развертывание микросервисов.Вы также поймете, как перенести монолитное приложение на микросервисную архитектуру, учитывая при этом масштабируемость и передовые практики. Кроме того, вы познакомитесь с несколькими важными методами DevOps, которые помогут вам перейти к более сложным областям, таким как разработка собственного облака, а также познакомитесь с некоторыми интересными шаблонами проектирования.
По окончании этой книги вы сможете организованно и эффективно разрабатывать приложения на основе микросервисов. Вы также получите знания, позволяющие преобразовывать любые монолитные приложения в микросервисы.
Стиль и подход
Эта книга, наполненная кодом, который вы можете сразу начать набирать, проведет вас через создание, тестирование, защиту и развертывание микросервисов наиболее практичным способом. Книга больше направлена на то, чтобы показать вам, как это делается, а не на том, что делать, хотя вы получите хорошее представление об этих инструментах, наиболее широко используемых для создания микросервисов.
Показать и спрятать еще
.Учебное пособие по микросервисам
: Архитектура и пример
- Home
Testing
- Back
- Agile Testing
- BugZilla
- Cucumber
- Database Testing
- 000
- ETL Testing
- JUnit
- LoadRunner
- Ручное тестирование
- Мобильное тестирование
- Mantis
- Почтальон
- QTP
- Назад
- Центр контроля качества (ALM)
- 000
- RPA Управление тестированием
- TestLink
SAP
- Назад
- ABAP
- APO
- Начинающий
- Basis
- BODS
- BI
- BPC
- CO
- Назад
- CRM
- Crystal Reports
- Crystal Reports
- FICO
- Заработная плата
- Назад
- PI / PO
- PP
- SD
- SAPUI5
- Безопасность
- Менеджер решений
- Successfactors
- SAP Tutorials
4
- Web
- Apache
- AngularJS
- ASP.Net
- C
- C #
- C ++
- CodeIgniter
- СУБД
- JavaScript
- Назад
- Java
- JSP
- Kotlin
- Linux
- Linux
- Kotlin
- Linux
js
- Perl
- Назад
- PHP
- PL / SQL
- PostgreSQL
- Python
- ReactJS
- Ruby & Rails
- Scala
- SQL
- SQL
- UML
- VB.Net
- VBScript
- Веб-службы
- WPF
000
000
0003 SQL
000
0003 SQL
000
Обязательно учите!
- Назад
- Бухгалтерский учет
- Алгоритмы
- Android
- Блокчейн
- Business Analyst
- Создание веб-сайта
- CCNA
- Облачные вычисления
- 00030003 COBOL 9000 Compiler
- 9000 Встроенные системы
- 00030002 9000 Compiler 9000
- Ethical Hacking
- Учебники по Excel
- Программирование на Go
- IoT
- ITIL
- Jenkins
- MIS
- Сеть
- Операционная система
- Назад
- Управление проектами Обзоры
- Salesforce
- SEO
- Разработка программного обеспечения
- VB A
Big Data
- Назад
- AWS
- BigData
- Cassandra
- Cognos
- Хранилище данных
- HBOps
- HBOps
- MicroStrategy
- MongoDB
0003
0003
.
новейших вопросов о «микросервисах» — Stack overflow на русском
Переполнение стека
- Около
Продукты
- Для команд
Переполнение стека
Общественные вопросы и ответыПереполнение стека для команд
Где разработчики и технологи делятся частными знаниями с коллегамиВакансии
Программирование и связанные с ним технические возможности карьерного ростаТалант
Нанимайте технических специалистов и создавайте свой бренд работодателяРеклама
Обратитесь к разработчикам и технологам со всего мира- О компании
Загрузка…
.