При наличии множества сервисов работа может легко превратиться в хаос — чтобы наладить пути коммуникации между ними, понадобится много ресурсов, а любое нововведение потянет за собой череду изменений, которые нужно будет вносить во все сервисы. При этом масштабирование в такой ситуации превращается в сущий кошмар. Чтобы упростить весь этот процесс, существует Enterprise Service Bus — архитектура, которая помогает навести порядок и облегчает межсервисное взаимодействие.  Всем привет, меня зовут Даниил Солодухин, я программист в студии ITT. В этом тексте я расскажу про особенности ESB, а также объясню, чем она полезна в работе. Прежде чем перейти к обсуждению ESB, давайте определимся с проблемой. Представьте, что у вас есть система из большого количества сервисов — каждый занимается чем-то своим, предоставляет часть функциональности всей системы. Какой-то занимается авторизацией, другие хранят пользователей, историю, данные кредитных карт и так далее. Это могут быть не микросервисы, а достаточно большие монолиты. Для нас разницы нет.  Сервисы общаются между собой простым способом — каждый вызывает или отправляет запросы тем сервисам, которые ему нужны. Получается такая схема, где каждый связан еще с несколькими другими.   Чтобы добавить пикантности схеме, напомню, что еще протоколы могут быть разными. Часть сервисов умеют работать по HTTP, часть используют JMS, часть применяют файловый ввод-вывод (они ждут файлов в определенных каталогах на своем сервере), а какие-то компоненты этой схемы вообще общаются по e-mail.  Если вы думаете, что я нагнетаю обстановку — нет. Это реальная картина, я с этим сталкивался: такое бывает, когда у вас достаточно старая система — сервисы вводили в строй в разное время, разными командами. И еще хуже, когда сервисы не ваши, а какие-то сторонние, и вам надо с ними общаться. Вы никак не можете повлиять на то, какие API, какие протоколы они будут использовать. Подобный хаос встречается повсеместно.  Одна группа исследователей заметила, что разработчики стремятся использовать примерно одни и те же подходы к решению проблемы. В 2003 году эта группа выпустила книгу Enterprise Integration Patterns — в ней исследователи собрали и структурировали все накопившиеся паттерны интеграции, которыми пользовались программисты. На картинке ниже представлено схематичное изображение всех собранных в книге паттернов. Наверное, вы увидите там некоторые знакомые термины: например, Message Bus, Channel, Message, Router, Point. Сейчас многие из нас пользуются этими паттернами в повседневной разработке, воспринимая их как нечто естественное и само собой разумеющееся, а все это пришло к нам оттуда.  Используя эти паттерны, мы можем превратить нашу хаотичную систему в нечто красивое и понятное.  Возможно, некоторым из вас эта схема покажется знакомой — это из-за того, что паттерны у всех примерно одинаковые и приводят к похожим решениям.  Это схема архитектуры ESB, которая позволяет нам упорядочить хаос, сделать систему более красивой, удобной, масштабируемой.  ESB — это подход к решению задачи интеграции, некоторый набор принципов, правил, паттернов и того, как ими пользоваться. Архитектура ESB ориентирована на сервис. Соответственно, она event-driven, то есть мы ориентируемся на то, что мы общаемся с нашими сервисами через сообщения — мы их посылаем и получаем.  Кроме того, ESB — это распределенная система. То есть это может быть или один сервер, который пропускает через себя все взаимодействие ваших сервисов, или несколько серверов. А еще это может быть федеративная система, когда у нас несколько отдельных серверов, на каждом своя ESB, и они подключаются друг к другу и обмениваются сообщениями. При желании мы можем ввести систему в состав еще более глобальной системы. Или, наоборот, взять и выключить какую-то часть.  С другой стороны, ESB — это конкретное программное обеспечение. Возникает своеобразный дуализм понятия — это и архитектура, и в то же время ПО. Вот небольшой список самого известного middleware ПО, реализующего архитектуру ESB: IBM IIB Red Hat JBoss Fuse Mule ESB Oracle Service Bus Apache ServiceMix Apache Camel Я отдельно выделю Apache Camel — его постоянно включают в список популярных ESB-решений, но дело в том, что он не является ESB. Apache Camel — это фреймворк для разработки интеграционных решений, в том числе и для разработки ESB. Более того, Apache ServiceMix и Red Hat JBoss Fuse используют Apache Camel как составную часть своего решения, которое в целом уже является ESB.  Мультипротокольность. Это решение той самой проблемы, когда у нас разные сервисы общаются в разных протоколах.  Представьте себе, что вы не используете ESB и вам нужно добавить еще один сервис, который будет общаться с несколькими другими по разным протоколам. Тогда вам нужно будет включить в него поддержку этих протоколов. Если же вам понадобится поменять какой-то протокол, то его нужно будет изменить во всех связанных сервисах. В такой схеме масштабирование превращается в головную боль.  ESB позволяет этого избежать — она берет на себя всю работу с протоколами, с их обработкой и трансформацией. Соответственно, каждый сервис может общаться с помощью какого-то одного протокола, например, по HTTP. Ему больше ничего знать не надо. Если он хочет отправить сообщение другому сервису, он делает это по HTTP на ESB. А дальше она преобразует HTTP, например, в JMS и наоборот.  Получается, что от сервисов скрыта внутренняя кухня: они не обязаны знать, кто как работает, их задача — отправить сообщение на шину. Соответственно, если у нас один из сервисов меняет протокол, к примеру, был HTTP, стал JMS, то нам надо поправить это в одном месте — на самой шине.  Количество протоколов, которые поддерживает шина, зависит от конкретных решений. Например, IBM ESB поддерживает много вариантов, Sprint Integration — мало. Подсчитать точные значения может быть сложно. Но тот же Camel заявляет, что в целом поддерживает порядка 250 различных протоколов и вариантов трансформации данных. Помимо распространенных, Camel поддерживает, например, Slack, Jira. То есть вы можете из вашей ESB создавать задачи в Jira.  Data Transformations. Даже если мы пользуемся одним протоколом, у нас могут быть разные форматы данных — один сервис может предоставлять ответ в формате XML, второй — в Json, третий вообще в бинарном формате. Все это надо как-то преобразовать, чтобы разные сервисы понимали друг друга. Эту задачу ESB тоже берет на себя. Многие ESB предоставляют достаточно удобные графические инструменты для Data Transformations. Например, у вас с одной и с другой стороны XML, но при этом разные XSD. Вы открываете графический инструмент и там наглядно показано, как все работает.  Вот так выглядит Data Transformations в графическом представлении. Я взял пример из Red Hat JBoss Fuse.  C одной стороны входящие данные, с другой — исходящий формат сообщений. Между ними можно настраивать маппинг из одного типа в другой.  Маршрутизация. Это одна из важнейших составляющих ESB. Допустим, нам нужно из одного сервиса отправить сообщение в другой. Тогда нам надо задать маршрут: к примеру, если пришло сообщение от этого сервиса — нужно отправить туда-то. Но ESB предоставляет гораздо более сложную логику маршрутизации. Например, можно добавить фильтры: «Если пришло сообщение, а там есть такое поле в таком-то значении, то отправим туда. Если другое значение, то отправим сюда».  Еще можно добавить мультиплексирование, чтобы одно сообщение разослать нескольким клиентам. Причем можем отправить сразу всем или какому-то конкретному списку. Можно сделать сплит сообщения. Например, пришло одно большое сообщение, мы его разбили на пять маленьких, каждую часть отправили в свой сервис. Ответы мы опять собрали в одно большое сообщение и отправили обратно. Эту логику на себя берет ESB.  Другая возможность — добавление условных маршрутов. К примеру, пришло какое-то сообщение, а там нет нужного поля, поэтому мы не будем его дальше обрабатывать. Также на уровне ESB можно встроить Load Balancer — Round-robin, Failover и так далее.  Вот пример маршрутизации с помощью Red Hat JBoss Fuse.  Здесь достаточно простая логика. В файле мы что-то прочитали. Дальше идет элемент Choice, то есть какой-то выбор, куда нам направить. И в зависимости от параметров мы можем отправить наше сообщение по одному или по другому пути.  Гарантированная доставка сообщений. Если сообщение было отправлено, то получатель его обязательно получит. Даже если его в тот момент не было «в живых», то когда он появится и подключится к ESB, ему это сообщение все равно доставят.  Здесь надо сделать небольшую ремарку. Camel не гарантирует эту доставку — если некому доставить, сообщение пропадет. Никто хранить его для вас не будет. Если вы пользуетесь Camel и хотите получить полноценную ESB, вам придется самим обеспечивать гарантию доставки.  Мониторинг. ESB позволяет в целом посмотреть, как у вас идет процесс: где в данный момент находится сообщение, куда уходит, помогает собирать статистику и так далее. Все это доступно из коробки (но не в Camel). Интегрированные средства разработки (IDE, low-code). Эта возможность позиционируется как чуть ли не основная «продающая фича» ESB. Она позволяет самостоятельно вносить какие-то изменения в процесс и не трогать для этого разработчиков. Например, сегодня вы решили, что сообщения должны идти в какую-то другую систему, а завтра поменяете обратно и все будет продолжать работать. Но проблема этого подхода в том, что разобраться в IDE не так уж просто. Обычный менеджер вряд ли захочет вникать в это.  Тем не менее возможность мышкой накидать эти элементы на какой-то Canvas выглядит достаточно интересно. Например, у нас на одном из проектов был очень большой процесс — он достался нам исторически, переделать его было просто невозможно, поэтому пользовались как есть. И нам помогала возможность быстро промотать мышкой и посмотреть, куда идут сообщения, как вообще устроен процесс.  В какой-то момент мы даже импортировали в картинку весь наш процесс в IBM Integration Designer, распечатали и повесили на стену — получилось полотно от потолка до пола, два на три метра. И если нужно было разобраться, как все работает, просто подходили к стене и карандашом проходились по стрелочкам. Но и недостатки у этого подхода тоже есть. Очень сложно работать с системами контроля версий. К примеру, если два человека одновременно поправили какой-то процесс и вам потом это надо смержить, то это боль.  У каждого элемента на графической схеме есть свой ID в виде порядкового числа. Если на схеме 100 элементов, а вы добавляете еще один, у вас появляется 101 элемент. Если вы добавите еще один, это будет 102 элемент. Соответственно, если два человека одновременно добавляют элементы, то у одного будет ID 101 и у второго будет ID 101. И когда вы начнете это мержить, то не факт, что Git вообще это воспримет как конфликт. А потом вы запустите приложение и обнаружите, что оно у вас не работает. Это только один пример, на самом деле там много возможностей для ошибки.   Мы в какой-то момент пришли к тому, что нельзя делать так, чтобы двум людям одновременно падали задачи, которые подразумевают исправления самого процесса. То есть надо заранее запланировать и декомпозировать задачи: чтобы один писал Java-код, а второй занимался исправлением схемы. К сожалению, пришлось изворачиваться. Если у вас большая команда (более десяти человек), вы, наверное, не сможете так сделать. Я так и не нашел нормального решения. Видимо, все так или иначе страдают.  Когда ESB стоит его использовать? Когда у вас много сервисов, все они имеют разные протоколы, разные форматы данных, а логика интеграции достаточно сложная.  Одна из самых приятных возможностей, которые дает ESB, это простое масштабирование этих систем. Если вы добавляете какой-то сервис, то будет достаточно изменить код или схему всего в одном месте. Вы можете в любой момент поменять сервисы, например, перейти с HTTP на JMS. И это повлияет только на сам сервис и на шину. Остальные части системы могут вообще не знать о том, что что-то изменилось. И это удобно.  Также масштабирование можно рассматривать не только в рамках одного сервера ESB, а устраивать федеративные системы. И в любой момент можно что-то переподключить, отключить, убрать и добавить.  Еще многие отмечают, что с ESB удобно вести разработку итеративно — последовательно подключать новые возможности, сервисы. Их можно добавлять с помощью федерации. Например, если вы собрали ESB с некоторым количеством сервисов, то впоследствии вы сможете расширить свою систему, подключив ее к более масштабной системе и все будет работать.