Микросервисы — модное словечко, которое практически не сходит с уст как ведущих архитекторов информационных систем, так и начинающих программистов. Стоит заговорить про аспекты реализации некоего бэкенда или новой информационной системы, как разговор моментально сворачивает в сторону микросервисов. Почему так происходит? В чём причины популярности микросервисов, почему и архитекторы, и разработчики единодушно кивают головами и говорят много хороших слов в пользу этой архитектурной концепции? Тому множество причин, но в этой статье мы попробуем рассмотреть одну, весьма необычную. А именно: сходство микросервисной архитектуры и довольно старой, ставшей уже едва ли не каноном, философии Unix.
О философии Unix
Unix-way, Unix-стиль или философия Unix до сих пор, что называется, «не отпускает» умы программистов, проектировщиков, инженеров и пр.
«Отцами» философии Unix называют Кена Томпсона и Денниса Ритчи. По справедливости, их можно было бы назвать отцами вообще всего того, что мы имеем в мире IT на сегодняшний день. Кен Томпсон, пионер компьютерной науки, один из создателей С и операционной системы UNIX, UTF-8 и языка Go. Деннис Ритчи, создатель языка С и UNIX, очень скромный и малозаметный гигант, на плечах которого стоит вся современная IT-индустрия и компьютерная наука.
Кен Томпсон, размышляя о том, как должна выглядеть современная операционная система, в итоге выработал некий свод неформальных правил проектирования программного обеспечения. Философия произрастала из практического опыта и не являлась чем-то абстрактным и оторванным от реальности. Фактически, философия Unix никогда не была ни неким стандартом, ни некоей методологией, а представляла собой бесценные кристаллы опыта разработчиков и пользователей.
Это очень ёмкая, простая, лаконичная концепция, умещающаяся в три основополагающих принципа, которые были в своё время предложены Дугласом Макилроем, программистом, автором концепции конвейеров в UNIX:
- Пишите программы, которые делают что-то одно и делают это хорошо.
- Пишите программы, которые бы работали вместе.
- Пишите программы, которые бы поддерживали текстовые потоки, поскольку это универсальный интерфейс.
По большому счёту, вместо сложных и замысловатых приложений мы имеем множество простых (так называемых утилит), комбинируя которые мы можем получить новые уникальные инструменты для решения практически любой задачи. Используя принципы UNIX мы, вместо громоздких ядер операционных систем, имеем базовое ядро, которое расширяется посредством модулей. Вместо монолитных архитектурных решений, которые с огромным трудом поддаются модификации и расширению, мы имеем гибкую, модульную архитектуру, позволяющую решить практически любую, как простую, так и очень сложную задачу.
Универсальность и гибкость в данном случае также обеспечиваются посредством обобщённых интерфейсов передачи данных. В UNIX их всего три: стандартный ввод, через который утилита получает данные, стандартный вывод — куда она выдаёт результат своего «труда», и стандартное устройство вывода ошибок — куда выводится информация об ошибках или процессе выполнения задачи. При этом стандартные устройства можно переназначать, перенаправлять. Например, вывод одной утилиты передать на ввод другой и т.д. Объединяя утилиты в так называемые конвейеры, где стандартные ввод и вывод многократно переключаются в процессе выполнения, можно решить практически любую задачу.
Посудите сами, насколько просто и изящно можно сделать резервную копию своей рабочей папки сразу же на съёмный носитель и на удалённую машину в сети:
( cd ~ ; tar czvpf - . ) | ( tee /mnt/USB_STICK/backup.tar.gz | ssh me@myhost 'cat > backup.tar.gz' )
Чтобы «изобрести» приложение с подобным функционалом программисту (или даже программистам) понадобится немало времени и усилий, а с применением утилит с такой задачей справился бы простой квалифицированный оператор без знания тонкостей программирования и отладки.
Поскольку каждая утилита сравнительно проста и призвана решать какую-то одну задачу, кодовая база такой утилиты является по большому счёту изолированной от других утилит либо ссылается на другой код или использует его в минимально необходимом объёме. Например, если говорить о стандартных утилитах Unix, то их поверхность соприкосновения — это стандартная библиотека LIBC. Такой подход позволяет практически любому программисту, едва ли не в одиночку, исправить какую-то ошибку, улучшить некий алгоритм утилиты и отправить «патч» автору по электронной почте.
Другой весьма интересной особенностью Unix-стиля является предпочтительное использование текстовой информации как для ввода данных, так и для их вывода. Возможно, это покажется немного архаичным и очевидным, но именно текст, который может прочесть человек на экране терминала, является наиболее простой и наиболее законченной концепцией полностью переносимых данных. Почему же не двоичная информация, ведь она занимает гораздо меньший объём памяти и обрабатывается значительно быстрее, нежели текстовая? Да хотя бы просто потому, что на разных аппаратных платформах присутствует различный порядок следования байтов (little- и big-endian), различные размеры целых и вещественных чисел и прочие несовместимости. Чтобы обеспечить бесшовную работу утилит UNIX на разных платформах, пришлось бы писать массу крайне хрупкого конвертирующего кода, что в целом пошло бы вразрез с принципами Unix. Исходя из этого, текстовый формат данных предпочтительнее, несмотря на сопутствующие преобразованиям данных накладным расходам. Но кто будет о них переживать, ведь они происходят где-то в недрах утилиты, в её коде, а нас как пользователей интересует в первую очередь результат.
Концепция текстовых интерфейсов оказалась весьма живучей. В конце концов, мы получили такие прекрасные средства, как YAML, XML и JSON – далёких потомков простых текстовых файлов, в которых данные разделялись пробелами или запятыми и располагались на отдельных строках.
Кто же из программистов и разработчиков не знает основополагающие принципы построения высокоэффективного программного обеспечения, скрывающиеся за скромным акронимом KISS — keep it simple and straightforward? Сегодня это едва ли не один из самых значительных и часто используемых сводов правил создания программного обеспечения. А в основе этих правил лежит всё та же ёмкая и очень понятная, практическая философия, заложенная в те далёкие времена, когда деревья ещё были маленькими, а компьютеры уже были огромными.
О микросервисах
Что же это за такие сервисы, почему они имеют приставку «микро» и почему они так популярны у разработчиков в нынешние дни?
Прежде чем мы перейдём к рассмотрению этой концепции и её интересной связи с древней, по меркам научно-технического прогресса, компьютерной философией, определимся, что же такое сервис вообще?
Концепция сервиса является краеугольным камнем так называемой сервис-ориентированной архитектуры или СОА. В этой архитектуре сервисы – это независимые друг от друга программные модули, которые решают свои, строго оговоренные задачи. Сервис-ориентированная архитектура не привязана к какой-либо архитектуре, она может существовать поверх таких известных технологий, как RPC, XML-RPC, DCOM, CORBA или WEB.
Сервисы СОА не имеют никаких специальных знаний о вызывающих их программах, а программы, в свою очередь, не «знают», как устроены эти сервисы и на чём они написаны.
Таким образом, СОА — это скорее стиль архитектуры информационных систем, который позволяет формировать их из слабосвязанных информационных сервисов. Эти сервисы взаимодействуют при помощи машинно- и программно-независимых интерфейсов, при этом определения последних скрывают языковые реализации сервисов.
Даже беглый взгляд на эти описания уже начинает зацепляться за некоторые весьма схожие с Unix понятия: слабые связи, единые интерфейсы, строго оговоренные задачи. Действительно ли концепция СОА имеет некую общность с философией UNIX или это кажущиеся сходства?
Если говорить о концепции СОА в общем, не вдаваясь в аспекты реализации, то сходства лежат лишь на поверхности. СОА не диктует нам то, как мы должны реализовывать свою информационную систему, какие языки, какие парадигмы и какие архитектуры мы должны положить в основу нашей реализации. Важным является лишь внешняя сторона — то, как наши сервисы взаимодействуют с программами и между собой.
Казалось бы, что в этом плохого, коли есть столь гибкая и подвижная доктрина?
Дьявол, как водится, кроется в мелочах, а именно, в реализации таких систем. СОА отдаёт реализацию на откуп инженерам и разработчикам. Рынок диктует свои условия; одним из факторов успешного продвижения программного продукта является быстрота его разработки. Однако, быстро не всегда означает качественно. Иногда времени на применение какой-либо концепции или архитектуры нет совсем и в большинстве случаев разработка «скатывается» в поддержку и развитие огромного программного монолита. От версии к версии код такого программного продукта разрастается всё больше, количество ошибок растёт. Проблема также усугубляется при использовании разных языковых средств и парадигм программирования, что в больших проектах не редкость: возьмём к примеру frontend и backend, которые часто реализуются при помощи разных экосистем.
Микросервисы в целом являются частным случаем СОА. Подобно ей, они также реализуют чётко определённые интерфейсы взаимодействия между собой и клиентами, каждый микросервис решает отдельную задачу и сами микросервисы не имеют сильных связей друг с другом.
Микросервисы в целом являются частным случаем СОА. Подобно ей они также реализуют чётко определённые интерфейсы взаимодействия между собой и клиентами, каждый микросервис решает отдельную задачу и сами микросервисы не имеют сильных связей друг с другом.
В отличие от набора правил СОА, микросервисная архитектура всё-таки предъявляет определённые требования к реализации, в противном случае она просто не имела бы никакого практического преимущества перед традиционными подходами. Можно выделить ряд принципов микросервисной архитектуры:
- Каждый микросервис реализует строго определенную задачу и сам является изолированным процессом, приложением или службой;
- Микросервисы могут развёртываться и запускаться независимо от других процессов, служб и даже инфраструктуры;
- Связи между микросервисами не должны быть сильными (strong), а должны быть слабыми (loose). Например: микросервисы должны использовать так называемые доменные (или предметные) связи между собой, которые лишь описывают взаимодействия между ними. Классический пример из мира программирования – это интерфейсы (interfaces);
- Приставка микро- не должна быть просто приставкой, сервис действительно должен быть микроскопическим, настолько, чтобы его мог сопровождать всего один человек;
- В идеале, хранилище данных каждого микросервиса должно быть доступно только этому микросервису и никакому другому компоненту информационной системы (своя база данных);
- Микросервис может быть «одноразовым» – он создаётся и используется лишь единожды, для решения какой-либо узкой задачи (например проверок гипотез или обработки данных), а в последствии устраняется из системы, без вреда для неё;
- Не накладывать никаких ограничений на выбор языков и сред для создания микросервисов.
Если теперь бросить взгляд на концепцию Unix, свод правил, которые на протяжении многих десятков лет определяли стиль и направление развития программного обеспечения, то что же мы увидим?
А увидим мы, что принципы, которые положены в парадигму микросервисов удивительно похожи на принципы Unix.
Микросервисы
- Унифицированный интерфейс приема параметров и возврата значений — в большинстве случаев это HTTP REST API.
- Один микросервис решает строго одну задачу.
- Предпочитается текстовый формат входных и выходных данных: машиночитаемые JSON, XML, YAML.
- Код микросервиса не зависит от кода других микросервисов, но может зависеть от общих библиотек, повторно используемых, общих компонентов.
- Микросервис вполне может сопровождать один человек.
- Связи между микросервисами слабые. Совокупность связей определяет бизнес-логику информационной системы.
UNIX
- Стандартизированный ввод-вывод: stdin, stdout, stderr.
- Одна утилита или модуль для решения конкретной задачи.
- Предпочитается текстовый формат входных и выходных данных.
- Код утилит, как правило, изолирован от кода других утилит, может использовать код общих библиотек, например LIBC.
- Автор утилиты или модуля — часто один человек.
- Связи между компонентами и утилитами слабые. Комбинации утилит, конвейеры, перенаправление ввода-вывода позволяют решить практически любую задачу.
Удивительным образом, концепция, которая насчитывает уже несколько десятилетий расцветаем прямо посреди современных доктрин построения и эксплуатации информационных систем. Очевидно, по этой причине микросервисы так полюбились и разработчикам и архитекторам, позволяя решать задачу простыми средствами и добиваться требуемого результата.
Есть ли отличия и в чём они заключаются?
Прежде всего в том, что микросервисы — это не философия, не квинтэссенция пользовательского опыта, не интуитивные прозрения и не творчество гуру — это архитектурный подход к реализации сложных и очень сложных информационных систем. При наличии большого количества схожих признаков, эти две концепции — философия Unix и микросервисный архитектурный подход — всё же идут разными путями. Причина кроется, на мой взгляд, в том, что круг задач, которые решают микросервисные информационные системы, и количество задач, информационных объектов, средств, архитектурных и инфраструктурных решений в значительной степени превышает тот объём, который стоял перед авторами концепции Unix в далёкие 70-80-е годы XX века.
В пользу этой точки зрения говорит то, что микросервисная архитектура, даже будучи столь законченной и лаконичной внешне, всё же продолжает эволюционировать. Продуктом такой эволюции стали бессерверные службы, такие, как OpenLambda, которые позволяют разработчикам вообще забыть про инфраструктуру, ядра и прочие вещи, а работать непосредственно с чистым кодом своего приложения. Вполне вероятно, что в будущем, подобные сервисы эволюционируют в чистого вида функции некоего языка программирования, а вся информационная система будет создаваться и проектироваться в средах, терминологии и концепциях, которые наиболее отвечают этому процессу, минуя сложные перипетии выборов архитектуры, способов развёртывания и управления такими системами.