Сейчас очень модно внедрять микросервисы, но не у всех это получается хорошо. В особенности, если речь идёт о больших энтерпрайзах и банковских системах. Кто-то годами не может распилить свой монолит, кто-то не уверен в отказоустойчивости и так далее. Сегодня мы поговорим о внедрении микросервисной архитектуры в Центре Финансовых Технологий (ЦФТ) — группе компаний, работающих в области информационных технологий для финансового сектора с 1991 года. То есть это организация, где качество продукта чрезвычайно важно, от него зависят реальные деньги.  В свою очередь, Владимир Плизга последние 6 лет погружен в разработку бэкенда интернет-банков и сопутствующих сервисов в ЦФТ, где активно топит за микросервисы и прочие модные штуки. Чтобы пообщаться с ним, я приехал прямо в офис ЦФТ, сделал сэлфи и обязательную фотку красного слона :-) Обсуждаемые темы: (слева — Владимир, справа — olegchir) — Зовут меня Владимир Плизга, работаю в компании ЦФТ уже несколько лет. В основном занимаюсь бэкенд-разработкой на Java. До этого совсем немного работал в других областях, но это можно отнести к студенческому и пост-студенческому периоду. Если быть более точным, то сейчас занимаюсь разработкой финансовых сервисов — и не какими-то там брокерами и котировками, как часто думают, слыша слово «финансы», а интернет-банками и сопутствующими сервисами. Например, такие инструменты, как сайты отслеживания подарочных карт — когда людям вместо наличных денег дарят универсальную брендированную карточку, которой можно рассчитываться везде, где принимают эти карты. К ней прилагается приложение, с помощью которого можно контролировать расходы по этой карточке, смотреть историю, активировать, прочие профиты получать. — Эта карточка-сертификат — она пополняемая? — Зависит от того, какая модификация продукта. Большинство из них не пополняются. Сам ее приобретатель, тот, кто будет ее дарить, один раз пополняет карту на какую-то сумму, которая ему комфортна, обычно от 300 рублей до 15 тысяч рублей, дарит её, и всё. Есть отдельные модификации, где можно в том числе пополнять. Но это не сертификат, а именно платёжная карта, это полноценный такой финансовый продукт. Но он скорей побочный, а основное для нас — это именно предоплаченные карты, то есть те карты, которые для людей являются своего рода дополнением к кошельку, либо к электронному, либо к настоящему. Туда можно любым удобным способом запуливать деньги, а потом — рассчитываться ими хоть в интернете, хоть в магазинах, хоть через сам платежный кабинет (он у нас называется чаще всего не «интернет-банк», а именно «платёжный кабинет»), на всякие нужды, начиная от базовых платежных сервисов типа «оплатить садик», «оплатить ЖКХ» и прочее — в общей сложности более 5 тысяч услуг поддерживается — и заканчивая всякими платежами с AliExpress и прочими. В принципе, любыми электронными. Моя роль во всём этом заключается в том, что на текущий момент через меня проходят именно архитектурные решения о бэкенде. Это всё, что касается микросервисов. Сейчас мы на пути ухода от монолита к микросервисам, мы постепенно отпиливаем кусочки и новый функционал уже не добавляем в монолит, чтобы следовать правилу (кому-то уже надоевшему): чтобы вылезти из ямы, надо сначала перестать себе её копать. Занимаюсь также другими вещами, например, код-ревью, через меня проходит много бизнес-задач, в том числе по принципиально новому функционалу. Ну и сопутствующие дела по производительности, по архитектурным улучшениям… В каком-то смысле типичная работа бэкенд-разработчика. — А ты прям архитектор-архитектор, рисуешь кубики в редакторе? — Нет, потому что сам всё еще расту из прикладных разработчиков, тех, кто непосредственно пишет код по кем-то придуманным кубикам, мне ближе отталкиваться от практических целей и реальности. Иногда приходится поднимать уровень абстракции и пытаться раскладывать это по каким-то кубикам и диаграммкам, но я стараюсь не витать в этих облаках, а быть более приближенным к тем реалиям, с которыми приходится работать. Если это микросервисы, то оно обозначается конкретными специфичными для него понятиями и парадигмами. Стараюсь особо в абстракции не вдаваться. — А всё-таки, UML-диаграммки рисуете? — Да, рисуем, но не вдаемся в крайности поддержки их синтаксиса, правильности и так далее. — То есть они не являются источником генерации кода. — Нет, не являются. — Просто у меня тоже был такой опыт, и возникает проблема, когда у тебя огромное количество микросервисов, тебе хочется иметь цельную картину того, что происходит. Как эту цельную картину удержать, как её зафиксировать перед глазами? — На текущий момент мы идем от обратного. Мы не по картинке генерируем микросервисы, а по микросервисам поддерживаем картинку. У нас есть схема, в каком-то смысле это какое-то подобие UML. Но подчеркну, что мы ориентируемся в первую очередь на легкость её восприятия, поддержки, и не усердствуем над поддержкой синтаксиса и правил. На текущий момент мы не сильно вдаёмся в это направление, оно нам более чем удовлетворяет. Наша схема содержит и гиперссылки на документацию, и сама по себе изобилует какими-то условностями, есть легенда о том, что, где и как обозначается, и благодаря этому мы в ней пока нормально ориентируемся. Проблем не возникает. — Она генерится динамически из исходников? — Нет, мы поддерживаем её в Confluence. Там есть плагин Gliffy, который позволяет на уровне абстрактных фигур достаточно легко поддерживать картинку. Перетаскивание одного блока не заставляет вручную перерисовывать всякие связи. Буквально вот такие базовые вещи пока нас удовлетворяют. За счёт того, что мы разделяем участки на отдельные диаграммы, нам нет необходимости поддерживать всю картину целиком, поскольку она и для восприятия в этом виде непригодна. Есть блоки, в которые можно провалиться и увидеть другой, обособленный участок, который тоже пригоден для непосредственного восприятия. — У вас система разработана так красиво, грамотно и аккуратно, что вот эти блоки дают вменяемую структуру? — На текущий момент — да. Не буду особо хвастаться, думаю, что сейчас это объясняется в большей мере молодостью системы, чем грамотной архитектурой :-) Разумеется, мы стараемся её поддерживать. Множество проблем, которые могут возникнуть — мы их априори представляем, готовимся, вооружаемся, но в таком глобальном виде задача ещё не встала, поскольку речь пока не идёт о сотнях микросервисов. — Сейчас сколько есть микросервисов? — Единицы десятков. — Уже неплохо. У многих и такого нет :-)  — Но мы всё еще в начале пути. — Какие у вас в этом начале пути стоят задачи? — Задачи с точки зрения технического развития, архитектурного? — Задачи с точки зрения чего угодно. Ты же здесь архитектор! — Я хоть и архитектор, но в первую очередь я отталкиваюсь от потребностей бизнеса, коль скоро они для нас первоопределяющие. Бизнес хочет получить ценность, прямо сейчас. Поскольку мы пытаемся быть agile, мы допускаем, что какие-то вещи могут даже не пригодиться. Если мы во что-то вкладываемся, мы заведомо понимаем, что мы экспериментируем, проверяем, прощупываем почву. Исходя из этого, нам нужно построить свою разработку и свое приложение так, чтобы любая доработка была максимально дешевой — настолько дешевой, чтобы её было даже не жалко выкинуть. Разумеется, в абсолюте этого быть не может, но тем не менее, благодаря текущему направлению мы можем позволить себе в том или ином виде запилить какой-то кусочек инкрементально, не вкрапив его в монолит, куда он врастет корнями и поменяет всё вокруг себя, а сделать такую нашлёпочку рядом. Поиграться, посмотреть, как оно себя будет вести, что оно из себя будет представлять с точки зрения ценности конечному пользователю, и потом, при необходимости — выкинуть. Исходя из этих трех приоритетов: скорость, дешевизна и независимость от других компонентов — мы и пытаемся разрабатывать компоненты дальше.  В чём это проявляется чисто технически, если переходить от громких слов к делу. Нужно стараться не наращивать монолит. Он у нас и так достаточно большой, мы его так и называем — «ядро», всё, что его касается — «ядерное», «ядрёное», как-то ещё… В общем, туда по возможности больше не лезть. Естественно, строить коммуникации с внешними клиентами для нас (это и веб, и различные платформы mobile) нужно без участия этого ядра и сопутствующих ему сателлитов. То есть заводить все соединения напрямую через точку входа, чтобы там на уровне маршрутов, на уровне балансировщика нагрузки, можно было в любой момент отключать, переключать этот трафик и как-то контролировать его более гибко, чем когда он в едином потоке заливается в ядро.  Плюс к тому стараемся выстроить саму разработку так, чтобы это было максимально дешево. У нас есть целый микросервис — он полноценный, рабочий, но не делает ничего. И служит он единственной цели — быть шаблоном для создания остальных. Мы его тупо копипастим, он уже всё умеет, в нём уже всё поднято, настроено, он адаптирован к CI тестовому, адаптирован для раскатки на боевом, и от него уже можно достаточно легко отталкиваться. Это сделано специально, чтобы разработчикам стоило минимальных усилий стартовать новые микросервисы, вплоть до того, что сегодня поигрался — потом свернул и выкинул.  — Этот шаблон на чём основан? На чем-то общеупотребительном, типа Spring Cloud, или что-то свое? — По готовым Cloud-решениям… Возможно, мы даже не пойдем по этому пути, а будем использовать отдельные библиотеки. Это, в первую очередь, Eureka, Zuul — стандартные решения для реестра сервисов, точки входа. — Вы это уже используете, или это направление развития? — Уже используем. Точка входа и реестр сервисов уже сейчас в эксплуатации. Чтобы обкатать это решение, мы зарулили на него не боевой трафик от конечных пользователей, а вспомогательный с колл-центров, тот, который не такой активный и нагруженный. А что касается базы самого микросервиса, то это Spring Boot, как, в принципе, и у всех остальных. Не знаю, можно ли его считать «голым». Разумеется, с какими-то дополнительными фреймворками, чаще всего — фреймворками того же Spring. Мы больше всего упираем на спринговые решения. — А вот вы прикручивали это самостоятельно или взяли, что есть? Например, Spring Cloud Netflix. То есть аннотации, RestTemplate и т.п. — Да. Сейчас опыт наших микросервисов в том, что точка входа, что реестр сервиса — с точки зрения прикладного кода — они очень маленькие. Почти всё сводится к тому, что мы добавили аннотацию, а вся остальная прелесть лежит за пределами Java-кода: это скрипты сборки, скрипты старта, и всё, что касается его окружения и адаптации к нашим реалиям, CI/CD и дальше по девопсу. Правда, в точке входа мы ещё своих фильтров понапилили для дополнительного контроля доступа, вопросов безопасности, обеспечения обратной совместимости API — в общем, есть у нас свои допилки. Но опять же, даже само понятие фильтра — это абстракция, взятая из самой библиотеки Zuul, которая втянута в Spring Cloud. — Понял. А всякие такие штуки, circuit breaker, собираетесь использовать? — Да, собираемся. Но тут такая ситуация, что собираться-то собираемся, а сами уже используем. Просто Zuul, в поставке Spring Cloud, уже включает в себя Hystrix, и он уже работает, и мы уже вынуждены с его настройками разбираться, учитывать, что у него таймауты настраиваются, мягко говоря, нетривиальными способами, кое-какие приседания приходится делать. Мы его используем, но не могу сказать, что мы это делаем максимально сознательно и всё контролируем. — Не было ли желания что-нибудь допилить? В Эврике, например. Или в Зууле. — Zuul, к счастью, оказался достаточно гибок, чтобы такого желания не возникло. А, нет, вру. Есть у меня пул-реквестик на GitHub. Хотя нет, извиняюсь, сейчас это просто issue, а пул-реквест в разработке. В общем, оказалось, что Zuul из коробки не поддерживает кастомные HTTP-статусы. Под «кастомными» я подразумеваю те, которые не стандартизированы самим RFC на HTTP-протокол. 431 — незачем, никак не задан, нигде не светится. Оказалось, что в Зууле они внутри в кишках заложились на то, что приходящий код, который проксируется через точку входа наружу, обязательно будет соответствовать одному из элементов перечисления, зашитого в этот же самый исходный код самого Зуула. И когда туда приходит HTTP-статус, для которого нет мапинга, он падает. Причём падает очень плохо, обходного решения нет. Я им написал, они ответили — ну да, наверное, так, попробуй пофикси. Просто я еще обнаружил с удивлением для себя, что вот это место, где происходит основная поломка, оно несет в себе чисто отладочную роль. Оно призвано выкусить кусочек запроса, чтобы его правильно залогировать, и вот это «правильно» подразумевает, что будет логироваться не голый HTTP-статус, а элемент вот этого самого перечисления. — Нужно развернуть обертку. — И получается, что из-за отладки, которая может быть и отключена, но на таком уровне всё равно работает, падает всё приложение. На самом деле, острота этой проблемы упала. Мы передумали связываться с кастомными HTTP-статусами. — Зачем они вам вообще были нужны? — Была одна задумка, если интересно — в двух словах расскажу. Мы хотели с их помощью передавать оттенки происходящих ошибок или вообще ответов от конечных микросервисов. У нас каждый бизнес-сценарий подразумевает массу всяких всевозможных отклонений от результата. И для клиента зачастую важно знать, чем закончился бизнес-сценарий и каждый его шаг. Это может быть как необходимость повтора попытки, позже или прямо сейчас, причем повтора как сознательного пользователем, так и автоматического — клиентом. Это может свидетельствовать о необходимости показа диалогового окна с предложением что-нибудь там поменять. Если привести конкретный пример, то можно взять пополнение со сторонней банковской карты, с которой мы перечисляем деньги на свои предоплаченные карты — в этом процессе несколько запросов, и некоторые из них способны ветвить дальнейшие действия по подсценариям. И вот такие штуки мы как раз хотели обруливать кастомными HTTP-статусами. Своими, прямо договариваться с собой на уровне API (весь API у нас документирован в Swagger) и там как раз и прописывать в формате: статус + что нужно сделать клиенту. Тема не зашла по нескольким причинам. Разных причин было множество, но одна из главных: на тот момент у существующего монолита было сложившееся API, было принято передавать оттенки ошибок не в виде HTTP-статуса, а в виде полей, в самом теле ответа. — А может, лучше поставить HTTP-заголовки? — Можно было бы и заголовки, но в любом случае, это потребовало бы существенного изменения в API, обратно несовместимого. Можно было извратиться и сделать его обратно совместимым, но с этим пришлось бы жить дальше неопределённый срок, поскольку у нас много достаточно больших клиентов. Всё это взвесив, мы поняли, что проще сейчас извернуться и адаптироваться под старый API, чем потом тянуть за собой огромный пласт дополнительной логики для поддержки обратной совместимости с теми клиентами, которые ещё не переползли на новый API. Это всё было непросто, масса дополнительных обстоятельств, которые до нас донесли разработчики клиентских частей, но в итоге мы остановились на таком решении: просто передавать в телах. — Ты сказал про Swagger. Какая у вас практика его использования? Насколько это хорошо? Есть ли там приятные или неприятные моменты? — Хватает и тех, и других. Осмелюсь сказать, что приятных чуть больше, коль скоро уж мы на нём. Во-первых, самое главное, что мы заполучили (хотя здесь заслуга Swagger не такая уж большая) — это простота описания. Так уж получилось, что монолит у нас построен на Play Framework. В Play хоть и есть плагины для генерации swagger-документации, но, как это всегда бывает, по историческим причинам их писали разные люди в разное время, и эти плагины непригодны ни для чего, они у нас не работают. Поэтому документацию приходилось писать вручную. Прямо спецификацию, прямо сидеть и писать руками. — Как большая часть мира сейчас и делает. — Но, вот как ни крути, согласись, что ты то же самое пишешь кодом, зачем ещё сидеть и руками повторять? — И еще раз в джавадоке. И четвёртый раз в тестах. — Да! Зачем? Очень хотелось на этом сэкономить, поэтому, когда мы уже пошли по микросервисному пути, мы не стали изобретать никаких велосипедов, проанализировали несколько решений. На текущий момент самым мэйнстримовым, самым развитым решением показался SpringFox. Знаешь что-нибудь про него? — Нет, ничего. Теперь вот название знаю. — Этот генератор Swagger-документации на основе описаний контроллеров в самом Spring. В частности, в Spring Boot. Но на самом деле, под Boot он не сильно заточен, а ориентирован на SpringMVC, откуда все аннотации берутся. Но со Spring Boot дружит. Идея в том, что структура API, набор методов, их параметры — уже описаны в контроллере, в виде его методов и аннотаций, и их можно извлечь. Это уже львиная доля работы, скелет документации. Всё, чего тут не хватает — словесного описания. Но очевидно, что его достаточно легко добавить аннотациями в самом коде. Ровно по этому пути и идет SpringFox — берет аннотации Swagger (точнее, из подпроекта swagger-annotation, это часть генератора), применяет эти аннотации к контроллерам на Spring (SpringMVC, или SpringBoot в данном случае). Потом интерпретирует на лету, генерит Swagger-спецификацию. И в чём еще одна прелесть — прямо в составе микросервиса, выставляет еще один эндпоинт (не на отдельном порту, а на отдельном контексте), на котором открывается Swagger UI (эти красивые зеленые HTML-формы), где представлена вся документация, готовая не только для чтения, но и чтобы можно было поиграться с ней — сразу из коробки работают кнопки «try it out», чтобы отправить запрос в микросервис. На первый взгляд выглядит действительно как магия: не нужно вручную писать спецификацию, и в то же время она доступна. Есть отдельный URL, по которому ее можно запросить и потом скормить какому-нибудь SOAP UI или Postman. Всё это уже есть. Плюс к этому, на порту каждого микросервиса развернута документация (не нужно где-то искать, вспоминать), пригодная для чтения. Ещё один плюс — любое изменение в коде повлечет за собой изменение документации. Это надо еще постараться что-то такое написать и не заметить, что в этом же месте ты пишешь о том же самом в виде текста. В этом, конечно, существенный плюс такого решения.  Но, признаюсь честно, оно неидеально. Не все гладко. Сам SpringFox хотя и хорошо ориентируется в аннотациях и в структурах запросов Спринга, но не идеально. В частности, есть проблемы с коллекциями. Когда из методов возвращаются коллекционные типы, то он кривовато это представляет в UI. К тому же он позволяет тюнить не все те параметры, которые предоставляет голый Swagger — есть хотелки, которые следовало бы учесть. Есть ещё ряд шероховатостей. Но в целом, вполне рабочий подход. Но правда, и нам для этого пришлось перегруппироваться. Если раньше мы не стесняясь добавляли в сами контроллеры какую-то дополнительную бизнес-логику (валидацию, например — если она не сделана на уровне аннотаций), то теперь пришлось на уровне команды передоговориться. Сказать, что контроллеры для нас являются входной точкой и источником документации. Никакой бизнес-логики прикладной валидации, например, быть не должно там. Иначе они становятся нечитабельными, на каждом методе куча аннотаций. Вместо того, чтобы быть одному методу @RequestMapping, внезапно появляются @ApiOperation, @ApiResponse и так далее — куча всяких разных аннотаций. — Половина листа кода аннотациями заполнена? — В реальности бывает даже больше. Но за счет того, что мы договорились, что бизнес-логику делегируем отдельным сервисам, это улучшило покрытие тестами и позволило провести четкую границу. Вот у нас есть контроллеры, и они решают две задачи: источник документации и связь с внешним миром. А всё остальное делайте уже в прикладной логике в сервисах. — Эту документацию всегда пишут программисты, или у вас есть документаторы? — Нет, мы пишем сами, только разработчики. — И это нормально всегда происходит, без шероховатостей? — Здесь, в микросервисах, нам это даётся очень легко. Все шероховатости были, когда вручную разработчики вели документацию для монолита. Постоянно что-то забывали, постоянно что-то разъезжалось, устаревало, мобильные разработчики ругались, потому что документация неактуальная — вот это действительно была жесть. А сейчас, когда оно само происходит, разработчики только рады.  — У вас продукты на какую страну ориентированы? — На Россию. — А на каком языке пишется документация? — На русском. У нас своя команда, нет иностранцев, в том числе в удаленных офисах, поэтому мы всё ведем на русском. — Эта документация, получается, генерится для работы внутри компании? Или вы внешним подрядчикам тоже это отдаете? — У нас был такой опыт, но не сейчас, и подрядчики у нас русскоговорящие. — Круто, очень везёт. — Я знаю! — Вы смотрели код этого SpringFox? Если очень захочется, его можно вообще пропатчить, или там адовая жесть? — Да, можно. Там есть, конечно, нетривиальные места. Но в целом он написан нормально. Не скажу, что отлично, но неплохо. Пара раз уже поднималась рука поправить всякие скользкие места, да времени пока не нашлось. А во-вторых, за ним стоят довольно активные ребята и сами его довольно неплохо пилят. Не так активно, как хотелось бы, но регулярно выкатывают какие-то обновления. Правда, пока что ещё ни одной нашей хотелки не покрыли. — Насчёт HTTP: вы стараетесь наложить ваши запросы на какой-нибудь определённую методологию, например, на REST? — Да, стараемся. Одно время, на самом старте движения к микросервисам мы очень сильно на это упирали. Обсуждали, проговаривали, были жаркие споры. — Но обычно это очень сложно, натянуть сову на глобус. Как у вас с этим? — У нас с этим как обычно бывает, исторически сложилось. Если ты помнишь, я уже упоминал про вынужденную поддержку обратной совместимости с прежним API, на котором у нас огромный парк клиентов. У сотен тысяч людей на телефонах установлен. И все наши потуги быть restful во всех отношениях сползли с глобуса за счёт того, что мы были вынуждены поддерживать обратную совместимость с тем API. А то API, мягко говоря, не совсем restful. Там используется только POST и GET, вне зависимости, удаляешь ты или редактируешь сущность. Такого понятия как «сущность», вокруг которого строится весь REST — тоже его не особо придерживались. Там такой подход больше, как в самой Java — есть какой-то метод, давай запихнем его имя в сам адрес. Вот оно присутствует в самом этом адресе, getBySomething. Этот подход не ужасный, он рабочий, нормальный. С REST получается, конечно, лаконичней, няшней, но какой-то прикладной огромной ценности я не почувствовал, при том что API достаточно большой, взрослый. Как мне казалось, он рисковал стать болтливым за счёт того, что нет никакой единой парадигмы, но в принципе получилось неужасно. И сейчас, несмотря на то, что мы держим в голове какие-то RESTовые фишки и стараемся их использовать, ту же ориентацию на сущности, пытаемся так проектировать адреса методов, чтобы во главе угла стояла сущность и потом уже она потом подразделялась на какие-то тонкости, следующие по слэшу вглубь запроса, всё равно приходится ограничивать себя этими аспектами обратной совместимости. То есть у нас такой полу-REST. — Stateless или stateful? — Микросервисы сейчас все stateless. С одной штукой разве что есть вопросы, потому что следующим этапом в ближайшее время станет развертывание в системе оркестрации. Планируем в Docker-контейнерах всё это выкатывать на Kubernetes. Там одно из главных требований, которое мы согласовали с нашей эксплуатацией, которое, в принципе, весьма естественное — stateless microservices. Причины понятные: чтобы можно было любой из них уронить в любое время и тут же поднять рядом другой, чтобы он мог подхватить запросы. Чтобы они были максимально легкими на подъем и на урон. — А если всё-таки будут сервисы, которые тяжелые, что с ними делать? Если k8s уронит сервис на 400 гигов в хипе... — Что делать с этими данными? — Что делать с данными, как правильно проектировать сервисы, чтобы они переживали такие миграции? — Подходов есть несколько. Один из тех, который практикуется у нас (хотя не могу сказать, что он хорош в этом кейсе) — использовать распределенные хранилища. Чтобы то место, которое ты назвал хипом, стало не просто хипом, а ещё и где-то между микросервисами хранилось. Классическое решение (не знаю, насколько классическое для всех, но для нас оно таковым стало) — это Hazelcast. Распределенное хранилище на основе Hazelcast. Сейчас так работает монолит. Он кластеризован, у него есть общее распределенное хранилище. Любой кусочек данных хранится на всех нодах. Если одна нода упадет, никто не потеряет эти данные (разумеется, если они успели реплицироваться). — Какая степень репликации? — Сейчас я тебе точно не скажу. — Скажем так, это фуллсинк или как пониже? — Зависит от данных. В каком-то случае мы используем фулл, а для менее критичных данных, которые легко повторить, там 2/3. То есть данные хранятся на двух из трёх нод (если в кластере три ноды). Но тут я могу в чем-то наврать. Это один вариант.  Второй вариант — если у микросервиса раздулся хип до 400Гб, и всё это ценные данные— всё ли в порядке с микросервисом? Действительно ли он должен себя так вести? — Он уже не «микро», это «жирносервис». — Похоже на то, что его нужно превратить в несколько других «микро». Или под ним должна быть персистентная подушечка, которая будет его от таких вещей защищать. Понятно, она сделает его менее поворотливым, но это зависит от ценности данных. Может быть, имеет смысл где-то держать их отдельно. Не обязательно в полноценной СУБД, может быть стоит использовать какие-то более легковесные решения. Но мы пока с такой проблемой не сталкивались, мы достаточно маленькими их держим. Нет такого, что упал и потянул за собой на дно кучу ценных данных.  — Что будет, если в середине цепочки микросервисов упадёт полностью или частично что-то, что активно используется соседями? Там будет какой-нибудь graceful degradation или что-то такое? — В большинстве случаев нет, но в некоторых местах мы всё же подстраховались. Но не теми средствами, которые встроены в тот же Spring Cloud Netflix, когда используются фолбеки в Hystrix — о них мы знаем, пробовали, но пока не было необходимости. Вместо этого мы обходимся своими средствами, прямо в коде знаем, что можно предпринять в такой ситуации. Где-то позволяем запросу оборваться в угоду другому запросу. Или какие-то данные подтягиваем из кэша, если они там есть и допустимо взять их более старыми, чем свежайшие из базы, в которую пошли и обломались. Всё зависит от ситуации. Но таких мест не очень много. — Просто я беседовал с разными людьми, и две основные стратегии для людей, у которых нет честного GD/DR: если у нас приходит миллиард китайцев и падает кусок графа микросервисов — либо просто лежим и даже не пробуем отработать миллиард китайцев, либо «всё пропало», начинаем в ужасе бегать кругами, кричать и пинать админов. Как у вас с этим? Что это будет у вас? — Думаю, это будет ближе к первому :-) — Во втором случае, когда у тебя нет обходного пути, как-то нужно устроить прод так, чтобы он никогда не падал вообще. — Ну, звучит-то да, хорошо. Но по факту, насколько это реализуемо?  — Можно так сделать? Вы так делаете? — Нет, как видишь. На текущий момент ещё нет. Мы вместо этого пытаемся обойтись более точечными решениям. Либо защищаем отдельные кусочки, наиболее критичные, например, тот же монолит попытались кластеризовать. Миллиард он не выдержит, но тройную нагрузку за счёт элементарной кластеризации вывезти может. В каких-то отдельных бизнес-кейсах, которые точно могут повалиться, но скорей не только из-за нагрузки, а из-за объективных причин (недоступности внешней системы) — мы пытаемся их обойти с помощью кэшей и чего-то ещё. Но универсального решения на все случаи жизни у нас пока нет. — С этим будет разбираться эксплуатация, которые — живые люди. — Да. Если это будет какой-то прям повал, миллиард китайцев, то несмотря на то, что есть куча мониторингов и всяких систем прикрытия, если они будут из одного китайского штата, их просто забанят всех разом, и они просто уже не смогут стучаться — есть и такие средства. Но это будет сделано уже в полуавтоматическом режиме, с участием людей. — Каким образом эксплуатация поймёт, что у нас что-то плохо? Я намекаю на мониторинг. — Мониторинги наставлены на самых разных уровнях. Начиная от времени выполнения определенных критичных SQL-запросов и заканчивая активностью воркеров на Apache вовне. На все эти мониторинги выставлены определенные алерты, которые срабатывают по уровням (зеленый, желтый, оранжевый, красный и т.п.) — в зависимости от этого поднимается степень тревоги. Есть какие-то пороговые значения. Имею в виду, пороговые с точки зрения времени срабатывания алерта — в каком сколько мы бываем. И на всё это завязаны отдельные службы эксплуатации и сопровождения, которые не смотрят на них, а имеют каналы, по которым система сама их уведомит в случае проблем. В любой момент они на связи и могут быть привлечены к разбору ситуации. — Эти мониторинги вы написали сами или используете какие-нибудь Спланки или ещё что-то стандартное? — Смотря о какой части системы говорить. То, что касается бизнес-логики — это в первую очередь то, что на Java — там мы используем готовые решения. Для монолита используется JAMon, для SpringBoot микросервисов нам пока достаточно Actuator (то, что с самим Бутом поставляется). А всё, что касается инфраструктурной области — это уже мониторинг самих времён выполнения в БД, или дальше, ближе к клиенту — то у сопровождения и эксплуатации свои инструменты используются. Я сейчас затрудняюсь назвать, что они используют — всякие безумные штуковины, в которые я даже и не вникаю, потому что они нас, как разработчиков, и не касаются. Здесь процесс построен таким образом, что эта область целиком и полностью под ними. Только когда идёт что-то не так или требуется консультация, мы вместе разбираемся. Но в целом, нет необходимости нам над этим работать с этими инструментами. — Если у нас идут какие-то ошибки в бизнес-логике, к чему мы имеем отношение: как выставить уровень угрозы, начиная с которого нужно сигнализировать с желтым и выше приоритетом? — Это решает сопровождение. Они хорошо представляют себе бизнес-ценность каждого сценария, и, исходя из этого, настраивают себе мониторинги. Например, если количество подтверждений платежей в час упало ниже порогового значения для этого времени суток и этого времени года (хотя обычно этого не происходит), то, естественно, поднимется какой-то алерт. Они знают и собирают статистику — у нас есть довольно серьезная система сбора статистики (как для бизнеса, так и для сопровождения), по которой мы можем понимать все метрики активности пользователей по каждому из аспектов продукта — подтверждение платежей, входы, выходы, средняя продолжительность сессий, количество сессий… На любую из этих метрик можно повесить алерты и мониторить. Если, например, число сессий резко подпрыгнуло, значит, наверное, к нам пришел миллиард китайцев. И там, естественно, разбираться дальше. — Вы не пробовали писать искусственные интеллекты, которые будут анализировать паттерны поведения? — Нет. Знаю, что тема популярная, но пока нет. Точнее, у нас в подразделении — нет, а вообще в компании есть команда, у которой есть проект по анализу временных рядов и поведения системы для мониторинга. Вот там как раз активно применяется Machine Learning, даже опенсорсить вроде как собираются. — У вас просто хорошая эксплуатация, справляется. — Это что касается автоматического мониторинга. Есть, конечно, мониторинг, который можно посидеть и посмотреть глазками. Когда хочется понять, что сейчас происходит, что нас ждёт. — Но ты же не можешь десять терабайтов логов смотреть глазками? — Ну тут про логи даже не идёт речь, они пишутся такими объемами, что туда приходится смотреть нечасто. Для этого мы используем в микросервисах Java Melody — такая энтерпрайзная шутка, которая отлично вписалась и в Spring Boot. Очень удобная штука, которая встраивается в виде веб-фильтра во все запросы, да и не только в запросы, собирает кучу полезных данных и очень компактно выставляет их в виде красивой странички прямо на борту микросевиса. В любой микросервис можно зайти и посмотреть, что там творится. Что касается монолита, то туда мы прикрутили как раз JAMon — он выставляет метрики, которые мы потом агрегируем с помощью Zabbix. JAMon — это какое-то полноценное самостоятельное приложение со своей веб-мордой, но оно оказалось настолько, как бы сказать… неподходящее, что мы использовали от него только лишь способность выдавать метрики, и агрегировали их уже сами в Заббиксе. — Подытоживая то, что ты рассказывал последние 20 минут: не оказывается ли, что разработка на основе микросервисов дороже в результате, чем монолит? И сложней, и сложней поддерживать, нет? — Нет… а что, откуда такой вопрос? — Смотри, у тебя есть монолит. Ты написал одну какую-нибудь джавовую апишку — просто интерфейс. Средствами Java что-то вызываешь, у тебя всё работает автоматически. А когда у тебя есть микросервисы, когда тебе нужно вызвать апишку, нужно возвести некую башню из микросервисов, мониторингов, выставить API, пробросить всё это, оттестировать, отлаживать — это же сложность на порядок выше. — Безусловно, так и есть. Но здесь не мы первые, не мы последние. Все с этими сложностями сталкиваются. Все, кто идут по пути микросервисов. Да, есть дополнительный отпечаток на сложности даже разработки. Есть отпечаток на сложности тестового развертывания. Раньше если мы один монолит подняли — все с ним играются, максимум раздублировали его несколько раз. Сейчас же каждый микросервис со своими заморочками. Разумеется, операционная сложность — на часть Ops от DevOps легли все эти мониторинги. А в нашем случае, поскольку это финансовый сервис — согласование доступов, внешних интеграций, всё это каждый раз проходит массу этапов. Но вместе с тем и профит велик. Мы получили множество свобод, которыми не обладали раньше. Мы как разработчики, и равно как всё те же Ops’ы — они сумели опробовать и применить множество тех инструментов и технологий, которые раньше им были недоступны, но которые реально могли давать профит прямо сейчас. Благодаря этому, пройдя буквально один-два раза, собрав шишки на первых двух микросервисах, которые реально выкатывались через кровь и слёзы, дальше дело пошло намного проще и быстрей. И это не только касается Ops-части, это и у нас было точно так же. Да, мы собрали кучу шишек, граблей и прочих приколюх, но потом выразили в виде шаблонного микросервиса и научились его дублировать, вкрапив в него весь опыт, который был пройден перед этим. То же касается и развертывания на тестовом CI. У нас появились шаблонные сборки для Jenkins, которые мы просто копипастим и размножаем.  Отвечая на твой вопрос — всё те же мониторинги, API, документация, прочие дела — всё это пошло по накатанной и не достаётся так дорого, как в первый раз. В общем и целом, система получается сложнее, она не упрощается по сравнению с монолитом. Но она становится другой. В первую очередь для восприятия. Если, работая с монолитом, ты так или иначе вынужден держать в контексте внимания его весь, то здесь и сейчас в микросервисной архитектуре, достаточно концентрироваться только на каком-то определённом участке. Определённом, понятном тебе сейчас. Он может включать необязательно один микросервис — два, три, четыре (как правило, не больше). Но это действительно всё, что тебе нужно. И даже если оно как-то меняется и эволюционирует, ты не обязан выносить всё на бой. Соответственно, пропускать через весь этот длинный конвейер всё, что у тебя накоплено раньше, со всеми вытекающими последствиями — потенциальными поломками, несовместимыми изменениями и прочими делами. Хоть и увеличив сложность всей системы, мы всё равно получили возможность упрощать себе её восприятие в каждый конкретный момент времени. На каждом этапе мы всё равно работаем с маленьким, более понятным и управляемым кусочком, чем раньше было с монолитом.  Это то, что рассказывал мой дипломный руководитель. Ему удалось много лет проработать на оборонку, он руководил лабораторией по проектированию радиолокационной станции на зенитно-ракетном комплексе С-300. Сейчас С-400 «Триумф» Россия активно продаёт, а его предшественник С-300 обладал уникальной радиолокационной станцией, которую он проектировал. Он руководил лабораторией (102 человека), где как раз проектировалось это устройство. Вот он рассказывал, что только лишь комплект документации базовой для этой радиолокационной станции представлял из себя пачку бумаги, условно говоря, высотой с человеческий рост. И вот, говорит, во всей это лаборатории не было ни одного человека, включая его самого, который бы представлял себе, как это всё работает в целом, полностью и детально. Ну не было просто такого человека, невозможно всё это уместить в одной голове. Но зато, вместо этого, система таким образом была построена, что можно было вникнуть в любой ее участок — он был понятным, локализованным, изолированным от остальных, и участок этот помещался в голове одного человека. Вот примерно то же самое делаем мы сейчас—— создаем микросервисы такими, чтобы они помещались в голове одного человека. При необходимости, конечно, снабжённые интеграциями со смежными системами, но не далее. Этого вполне достаточно. — А у вас есть такие богоподобные существа, которые понимают большую часть системы, а не только маленькие кусочки? — Ну, на верхнем уровне, да и благодаря длительному опыту работы с монолитом — есть, это я и другие люди, которые достаточно давно в проекте. Да, мы в целом можем сейчас сказать чуть ли не за любой микросервис или проект. Поскольку мы просто уже достаточно долго с ним поработали и успели окунуться в каждую из этих областей. Но у нас нет стремления становиться богами, и действительно стараться всё под себя подминать, держать в голове, всё контролировать, вплоть до каждой строчки кода. Напротив, хочется дать некоторую свободу и себе, и остальным разработчикам, но при этом иметь возможность сконцентрироваться на выделенном участке и понять, что в нём сейчас происходит. А общую картину, разумеется, мы видим, мы ее контролируем, но на том уровне, на котором видна общая картина, нет необходимости вникать вплоть до строчек кода. Достаточно оставаться на верхнем уровне, на уровне межвзаимодействия микросервисов, на уровне каких-то их общих характеристик: какая база под ним, есть ли она вообще, какой у него API — общих чертах. На верхнем уровне этого более чем достаточно. — Я часто видел такую штуку, что, когда люди пытаются спроектировать большую сеть микросервисов, им нужно спроектировать протокол общения между ними и сущности, которые будут передаваться. И часто это выливается в создание единого реестра сущностей для всей организации, корпоративная модель данных, на все будущие года на 100 лет вперед. Это сложно и неприятно. Как у вас с этим справляются? — Пока что в этом вопросе мы на начальном уровне. Как только кому-то с кем-то нужно договориться, он садится и договаривается. Никакого реестра специального не ведется. Есть API, есть, как правило, клиент, есть кто-то, кто выступает сервисом. Один микросервис собирается идти в другой. Ребята садятся, как мы с тобой сейчас, договариваются, обсуждают, что-нибудь рисуют (здесь или на доске). Потом тот, кто за сервер отвечает — он обычно исходит из необходимости клиента — идёт и рисует API. Благодаря тому, что мы на SpringFox и Spring Boot делаем это достаточно быстро и дешево. За контроллером может ничего не скрываться, но API уже будет. И этот API выставляет в виде Swagger UI, скидывает письмо на клиентов: смотрите, я запилил API, устраивает? Клиенты смотрят, играют с ним, просят что-нибудь подтюнить. И, в принципе, всё. На этом какой-то формальной работы больше и не делается. Вместо этого потом делается описание микросервиса в целом, как технического решения, и это описание добавляется просто в базу знаний, в Confluence, где всё это копится. Но это никакой не реестр интерфейсов, а просто библиотека описаний микросервисов. Там, разумеется, есть ссылка на документацию, и какие-то пояснения к ней, если они не заслуживают места в самом Swagger UI. — Но вся эта идея основывается на том, что у вас везде HTTP, REST и так далее. — На этом, и на той идее, что мы достаточно близки друг к другу. Если бы речь шла о взаимодействии со внешними заказчиками, в других часовых поясах, странах и других компаниях — это бы точно не работало, этого было бы недостаточно, стопудово. — Есть ли у вас какие-то ещё форматы, кроме HTTP/REST? Например, как минимум, часто используют SOAP. — Безусловно, но тут надо уточнить, что такое «у нас». Мы как клиенты — да, ходим в другие сервисы, внешние системы. Для нас они внешние, а на самом деле 95% — это другие сервисы нашей же компании, других подразделений. Там у нас зоопарк. Там мы чем только не ходим. SOAP — это, пожалуй, самое безобидное, что есть. — Есть что-то хуже? — Да, бывают, например, сырые TCP-сервисы. Но это необязательно на Хабре рассказывать, чтобы я потом не краснел :-) Но как мне кажется, это объясняется старостью некоторых сервисов — когда-то их так запилили, они много лет работают, так сложилось. — Может, кому-то хотелось супер-скоростей. Посмотри доклады на HighLoad, там люди такое делают и считают, что это хорошо и правильно. — Есть такие, есть, допустим, суррогатные протоколы, где вроде они HTTP, но в телах передаются XML. Но это не WSDL, потому что они ничем не специфицированы. Просто кто-то в незапамятные времена договорился, что она будет вот такая. Есть договоренности по типу «запросы начинаются на req, ответы — на ans». Есть вот такие кейсы. Всякого хватает. — Стандартный кейс, когда тебе хочется передать большой файл. Как ты его будешь стримить? Или как — к запросу приложить файл, 4 гигабайта видео? Что делать? — Боюсь тебя удивить, но у нас в финансовых сервисах видео почему-то не передают по четыре гигабайта! Но я понял твой вопрос, просто потроллить маленько. Недавно решали похожую задачу, и во внешние системы ни по одному из перечисленных мною сейчас протоколов нам передавать такие файлы не приходилось. Мы, как правило, обмениваемся именно документами, XML. Если они из-за чего-то и разрастаются — за счёт цифровой подписи и контента. А что касается взаимодействия с конечными клиентами со стороны API, то там да, приходится принимать файлы, сканы документов, заявления и прочее. Такие вещи мы решаем обычным multipart/form-data, просто кусочками перегоняем данные, и всё, что контролирует — чтобы ни в каком месте микросервиса точки входа или каких-то промежуточных местах — чтобы нигде не было буфера, в который файл стягивался бы целиком. Мы в каждый момент работаем только с кусочком. Кусочек получили — кусочек обработали. Если надо в базу положить, под это заводится CLOB и BLOB (в зависимости от того, бинарные или текстовые данные) и пишется в виде input или output-стрима. Убеждаемся только, чтобы ни в какой момент времени файл не был в памяти целиком. Потому что здесь один файл, там один файл — и хип закончился. Тут нет никакой магии, по сути всё уже есть, мы постарались это грамотно применить, и те же парсеры стараемся настраивать. Не всегда, если есть риск большого файла — настраиваем именно стримовые парсеры, а не DOM, которые втягивают в себя всё сразу. — Глобально, какие ты видишь направления развития идеи микросервисов? Не только может быть, у себя в компании, а всего мира целиком? На что сейчас стоит смотреть людям? Что есть хорошего? Что ты уважаешь? — Первое, что приходит в голову — это уважение к opinionated-подходам, на основе которых построен в том числе Spring Boot. Подходы и инструменты (неважно, для микросервисов или сопутствующих задач), основанные на агрегации и обобщении чужого опыта. Как показывает наш опыт, это чуть ли не самое бесценное дополнение, которое только можно получить от сторонних инструментов. Когда они не просто спроектированы «пилите, что хотите, вот мы вам предоставили какие-то базовые абстракции, а теперь вытворяйте, что хотите». Вот яркий пример — Ant таким был. Очень мощный, очень гибкий инструмент, но что ни сядь делать, всё с нуля пиши, и сам постоянно накапливаешь... — Или мейкфайлы. — Да, мейкфайлы. Вот Gradle, на мой взгляд, очень удачное решение. Они очень много туда заложили того, что уже себя доказало, подтвердило как рабочее решение. Начиная от элементарной структуры директорий, которую они считают умолчательной, зачем ее декларировать явно и изобретать велосипеды? Это ещё и приводит к зоопарку, когда люди не обладают достаточным опытом, а потом выясняется, что оно не соответствует общепринятому, и начинается полный коллапс. Поэтому вот эти умолчательные opinionated-подходы чрезвычайно ценны как для начинающих команд, так и для продолжающих, тем, что они позволяют (иной раз даже не понимая этого) переиспользовать огромный богатый опыт других людей, которые уже прошли этой дорогой, собрали грабли и минные поля. Поэтому даже не упирая ни на какие конкретные инструменты или сервисы, в качестве глобальных идей я вижу превосходство opinionated-подходов. — Объясни слово opinionated, чтобы мы точно понимали, о чем ты сейчас. — Подход, основанный на мнениях. Например, припоминая, как писали ребята Spring Boot, почему разработали его именно таким, каким его имеем мы сейчас — они ничего нового не изобрели, а обобщили свой опыт. По их мнению, базовый проект, продукт должен выглядеть вот так. По умолчанию. Это не означает, что оно по любому будет выглядеть так, и если не соглашаешься — идёшь лесом. Это значит, что если ты не хочешь заморачиваться, то оно будет выглядеть вот так. Это их мнение. А дальше тебе предоставляется возможность подтюнить его как-то, подфиксить, подправить — под свои нужды, уже под своё мнение. В этом сочетании богатого опыта и гибкости и заключается максимальная ценность таких решений.  Не всегда это хорошо. Пример первого Play (со вторым мало работал) — он тоже opinionated, но там у ребят было очень специфичное мнение. Не знаю, что за хипстеры его писали :-) — Это тот, который на Java ещё? — Да. Не работал с ним? — Работал, но это было так давно, вся жизнь прошла. — Там, если помнишь, они почему-то не приемлют стандартные структуры Java-пакетов. Они считают, что если ты кладёшь что-то в пакет, то клади прям здесь, прям в корне. Вот у тебя называется controllers, не какой-то там org.company.app.controllers, а просто controllers — сколько же крови это нам попило! — Как разруливать клэши, когда нет структуры? Ты можешь структуру в имени класса организовать… — Ха, «отличное» решение! И вот такой жести там сплошь и рядом. Или, например, они считают, что весь веб — статический. Поэтому все методы всех контроллеров там статические. Ну вот, по их мнению, это так. Это просто пример. — Но они таким образом снизили сложность на разработку, сфокусировавшись на каком-то конкретном кусочке. И если ты вышел из этого счастливого кусочка, тебе капец. — Наш опыт показал, что выйти за этот кусочек очень легко. Как только у тебя приложение превращается из хомяка или лабораторной работы во что-то чуть более серьёзное — тут же всё, ты тут же за этой зоной. — И поэтому они написали второй Play, и даже переписали его на скале. Кстати, я верно понял, что вы в монолите используете первый Play? Ну и что, будете продолжать его использовать или возьмете что-то вместо этого? — Вместо этого мы будем постепенно выкашивать кусочки из монолита, пока его ценность не снизится до нуля. — Но веб-фреймворк всё равно какой-то нужен, чтобы морду показывать? — Нет, не нужен. В нашем случае монолит — это только бэкенд-ядро. А передняя часть у нас изолирована ещё одним приложением-сателлитом. А во-вторых, за веб-часть у нас отвечают отдельные ребята, которые полностью живут в своём мире и пилят веб-часть именно как самостоятельное веб-приложение на React.js. Мы им предоставляем только API — никаких шаблонов, HTML, ничего там такого не летает — голый API, который они используют в своём приложении. С мобилками, естественно, то же самое. Мы здесь не привязаны ни к какому фреймворку, лишь бы API соответствовал. А API — это HTTP. — Звучит как сказка, многие с зависти сгорят. — Ну не знаю… Мы это выстрадали, первая версия интернет-банка была старенькой, и мы её давно уже переписали, она была на фреймворке Wicket, слышал про такой? — Да, это ужасный фреймворк, я на нем работал много лет, я ненавижу его всей душой. — Могу тебя понять, я тоже его изрядно застал. Ты помнишь, что там есть понятие компонента, оно вписывается в HTML, есть wicketId, который потом на лету генерится, заменяется, вот это всё? У нас такое было, и мы оттуда ушли. Мы понимали, куда мы идём, от чего уходим, что нам надоело. В принципе, фреймворк не безнадёжен, где-то даже хорош, просто всему своё место. — Насколько понимаю, Wicket очень сложно масштабировать. — Честно говоря, вообще никак. — У них в корни иерархии классов есть интерфейс IClusterable, т.е. теоретически ты можешь работать от него, притащить Терракоту, но сами разработчики никогда в это не упирались. — Мы даже не дошли до этого — просто переписали всё и стали кластеризовать нормально.
Хотя некоторые наши приложения всё еще хорошо работают на Wicket. — Мы уже больше часа говорим. Не было ли идеи сделать из всего этого доклад? — Пока не было. Для меня всё это настолько близко, что всё это кажется какими-то обыденными вещами. Приду я, расскажу то, что все делают? Но с другой стороны, другие приходят, рассказывают, и ничего. — Есть большая разница: у других это ещё и не получается, а у вас получается. — Просто помимо того, о чём я здесь рассказывал, внимание занимают другие вещи. Например та тема, про которую я буду рассказывать на JBreak. И ещё у меня более свежий проект есть, где я разработал инструмент для агрегации серверных логов в тестовом окружении. В связи с развитием микросервисов стало актуально смотреть логи все в одном месте, как они пишутся, причем прямо сейчас, а не какие-то проиндексированные. С одной стороны, уже изобретена куча решений типа ELK, Logstash, Kibana и прочее, но как показывает опыт, на тесте — это стрельба по воробьям из пушки. Нужно настраивать форматы, индексаторы, они не всегда в риалтайме работают. Сама Ёлка — это, извиняюсь, три приложения, каждое из которых надо задеплоить. Нужно какое-то более легковесное решение, которое можно быстро задеплоить и которое позволит быстро и просто посмотреть всё, что в логах на сервере. Причём, очевидно, на удалённом сервере, у которого даже UI нет. Всё, что там можно сделать — позвать tail.  — И для этого нужен доступ по SSH, да? — К ним возможен доступ только по SSH, ну и по SFTP, других вариантов нет. И вот как раз с этой задачей я давно уже столкнулся, сделал простенькую утилитку, которая на тот момент просто поллила эти файлики и возвращала это в веб-страничке, которая открыта у тебя в браузере. И она прямо в риалтайме пишет. Такой tail, только браузерный. Никаких приложений не надо ставить, оно просто получает и поллит. Но этого оказалось недостаточно, когда речь зашла о микросервисах. Понадобилось какое-то решение, чтобы их агрегировать. И вот как раз на Spring Boot я запилил приложение, которое запускает tail, асинхронно слушает его, и, как только там появляются сообщения, агрегирует их и отправляет на веб-страничку уже приложению на Angular. Поскольку я не веб-разработчик, не стал заморачиваться, на Angular всё запилил. — Знаешь, веб-разработчики тоже иногда на Angular пишут :-) — Да, просто в последнее время принято считать, что Angular — это уже всё не то, надо что-то более свежее. — Второй или первый? — У меня первый, потому что на StackOverflow больше всего вопросов про него было. Собственно, там решаются разные попутные задачи. Как распознать временные метки, чтобы в конечном логе всё обязательно было по порядку, несмотря на то, что они пишутся неравномерно. Чтобы они не пересекались, т.е. если в сообщении стектрейс — чтобы посреди него не выползло чужое сообщение из другого лога. Происходит агрегация, распознавание временных меток, и всё это выстраивается в последовательность. — Ну, вроде всё обсудили. Следующий шаг — прийти на твой доклад. Спасибо за ответы, Владимир! Деврел Axiom JDK, продюсер в Failover Bar