Kafka. WebClient. Feign. WebSocket. Или как общаются микросервисы

от автора

Начнем с того, как микросервисы могут общаться? На самом деле все просто, сложные приложения могут состоять из нескольких разных микросервисов.

Каждый сервис будет иметь свою логику, свою ответственность. Сервисы одной системы могут быть написаны на разных языках программирования. Однако это не будет мешать им общаться. Так вот общение это буквально — обмен информацией. Обмен сообщениями определенного формата, который смогут понять все сервисы. Это похоже на общение между нами. Я говорю что-то собеседник слушает информацию, дальше обрабатывает ее неким образом своим мыслительным аппаратом и формирует ответное сообщение и проговаривает его вслух адресуя голос в направлении оппонента. Для отправки сообщения нам людям, нужно знать адресата или видеть его, для того, чтобы обратиться к нему.

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


Ох уж эти Сервисы Экстраверты, ни минуты не могут провести молча.

Ох уж эти Сервисы Экстраверты, ни минуты не могут провести молча.


Теперь давайте поговорим буквально о существующих инструментах общения. Можем разделить на две модели:

  • Первый вариант. Общение посредством прямого обращения к существующим методам другого сервиса по URL. То есть мы. знаем, что такой то сервис имеет определенные методы в которые мы можем обратиться. Посредством контроллеров по углам мы можем отправить понятный запрос и получить предполагаемый ответ.

  • Второй вариант. Общение посредством прокера сообщения например Kafka/Rabbit. тут все немного сложнее, так как нас как отправителя не заботит как будут обработаны сообщения, нас не интересует ответ на наше отправленное сообщение. Нас интересует адрес куда будем направлять сообщения и адрес откуда считываем. Простыми словами, есть хранилище сообщений где все разлажено по полкам, каждый полка подписана(topic) и мы можем направлять сообщения на одну полку или считывать сообщения только с определенно полки. Прямого взаимодействия с сервисом у нас нет. 

Итак разберем как общаться сервисам в первом варианте. Инструменты,к которые вы можете использовать

Rest Template — Блокирующий HTTP-клиент. Прост в использовании, но не рекомендуется для новых проектов.

WebClient — реактивный. Неблокирующий асинхронный клиент на Project Reactor. Идеален для высоконагруженных систем. Обеспечивает асинхронное/реактивное общение через HTTP

Feign Client — декларативный. Описываете интерфейс с аннотациями — Spring генерирует реализацию. Минимум кода. Обеспечивает синхронное общение через HTTP (REST)

Декларативность — это парадигма программирования, когда мы фокусируемая на том что нам нужно сделать, а не как. То есть декларативность обеспечивает вам легкую и понятную реализацию, где вы не лезете под капот, а пользуетесь доступными инструментами.

Вышел вы могли обратить внимание на такое свойство WebClient как РЕАКТИВНОСТЬ и давайте на этом моменте остановимся. В своей статье про многопоточность, я касался темы синхронности и ассинхронности. Поэтому останавливаться на этом я не буду а про реактивность давайте поговорим. 

Реактивность — это возможность системы реактивно реагировать на обновление данных. Соответсвенно реактивная обработка потоков данных невозможна без асинхронной обработки. Однако не только параллельная обработка данных достигается реактивностью.

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

— Ключевая особенность реактивности — backpressure (обратное давление): потребитель данных может замедлить работу поставщика данных при необходимости.

— Реактивность даёт декларативные операторы (map, filter, flatMap, buffer), позволяя красиво и лаконично преобразовывать потоки данных. Вы скорее всего подумали, что речь идет о Stream API. Но нет просто в реактивном программировании есть инструмент Flux<T> который так же как и Stream API работает как поток данных однако может обрабатывать поступающие данные, через такие же методы асинхронно. То есть работает с поступающими данными асинхронно.

Flux<User> users = userRepository.findAll(); Flux<String> names = users .filter(u -> u.getAge() > 18) .map(User::getName) .take(10); // ПОДПИСЫВАЕМСЯ и только после этого начинаются обращения в БД names.subscribe(name -> { System.out.println(«Получено имя: » + name); });

Посмотрите на код выше, сразу обозначим разницу от Stream API, Flux — мы можем получать прямо из БД, это всего лишь возможность запустить поток, по которому пойдут данные. Далее мы запускаем инструкции с помощью команд (map, filter, flatMap, buffer), но все еще в БД нет обращений. И только на этапе подписки .subscribe(name -> мы начинаем вытаскивать данные и обрабатывать их. Вот мы слегка коснулись реактивного программирования

— В Java реактивность реализуется через Project Reactor (Mono/Flux) и Spring WebFlux, но она нужна только при высоких нагрузках или потоковой обработке — для обычного CRUD это избыточно.

Теперь рассмотрим детальнее Feign Client. Это простой в использовании инструмент. Вы определяете Java-интерфейс с аннотациями Spring MVC, а Feign автоматически генерирует реализацию HTTP-клиента. Минимум кода, все реализовано под капотом за счет аннотаций вручную не нужно писать HTTP запросы. Есть возможность кэширования. Feign Client синхронный.


Feign Client 

Feign Client 


Если сервис должен ждать ответ прямо сейчас — Feign проще. Если нужна максимальная пропускная способность — WebClient.

Так же есть такой инструмент как WebSocked. Это способ общения посредством событий которые приходят в открытое соединение. Теперь нет никаких запросов. Есть канал для общения между сервисами, можно реализовать формат подписок. Один сервис бесконечно отправляет сообщения, другой считывает.

Если Feign и WebClient — это когда ты звонишь другу, задаёшь вопрос, получаешь ответ и кладёшь трубку, то WebSocket — это когда вы встретились в кафе и болтаете без остановки. Оба могут говорить когда хотят, не дожидаясь вопроса.

WebSocket часто используют для чатов, игр, биржевых котировок — там, где данные должны появляться мгновенно, без постоянных «стуков» от клиента. Соединение одно, а сообщения летают туда-сюда. Но важный момент: если соединение оборвалось, данные могут потеряться. WebSocket не гарантирует доставку, как Kafka. Он про скорость, а не про надёжность.

И вот мы плавно перешли мощнейшему инструменту Kafka.

Kafka

Kafka

Кафка — это хранилище сообщений, распределенный брокер, который может хранить терабайт событий. И обещает не терять их, раздавать сотням потребителей. Если сообщение ушло в Кафку, можешь быть уверенным оно там и останется. В Кафке модель общения происходит через Consumer (Потребителя) и Producer (Отправитель). Сервис формирует сообщение и отправляет через продюсер сообщение в топик (topic — это подписанная полка в памяти, куда направляются сообщения, в Кафке может быть множество топиков, обычно их разделяют чтобы разбивать информацию по блокам).

Почему Кафка — это мощно?

Потому что она даёт гарантии. At-least-once — сообщение не потеряется, но может прилететь два раза (если консьюмер упал после обработки, но до коммита). Для большинства сценариев — ок. А если нужно ровно один раз — есть exactly-once (через идемпотентность и транзакции). Кафка пишет на диск, реплицирует на несколько брокеров, один упал — другой подхватил. Ничего не теряется.

Идемпотентность — это свойство события отдавать один и тот же ответ на один и тот же запрос в не зависимости от количества запросов. Например: вы запрашиваете запросом объект с Id 9 — метод всегда будет возвращать один и тоже объект ответ будет одинаковым. А есть метод пост, вы отправляете запрос на создание и ответ всегда разный, вы создаете сущность, каждый раз с разными значениями, но значения Id будет меняться ведь вы создаете новую запись.

Порядок внутри партиции — полный. Если тебе нужно, чтобы события одного пользователя шли строго по очереди — бросай его ID в ключ, и Кафка сама положит их в одну партицию. Так же ты можешь настроить consumer group — запустил несколько консьюмеров, и они разберут партиции между собой, работая параллельно.


Kafka

Kafka


Когда использовать Kafka? 

Спойлер — всегда, когда нужно обмениваться событийными сообщениями между мервисами.

  • Когда у тебя события летят пачками, и сервисы не должны зависеть друг от друга (отправил email, начислил бонусы, записал в аналитику — всё асинхронно).

  • Когда нужно перечитывать историю (например, на лету пересчитать метрики за прошлую неделю).

  • Когда ты строишь потоковую обработку (Kafka Streams, ksqlDB) или ловишь изменения из БД (CDC).

  • Когда надёжность важнее миллисекунды задержки.

Минусы тоже есть

  • Сложная в настройке (кластер, партиции, репликация, zookeeper или крафт — это боль, но оно того стоит). На самом деле базовая настройка не очень сложная, но гибкая настройка требует от тебя внимания к деталям.

  • Задержка выше, чем у прямого HTTP (не мгновенно, но для 99% асинхронных задач — незаметно).


Kafka

Kafka


Итак, мы разобрали основные способы общения микросервисов — от простых синхронных вызовов до реактивных потоков и брокеров сообщений. Каждый инструмент закрывает свою зону ответственности. Feign — когда надо дёрнуть и получить ответ здесь и сейчас. WebClient — когда запросов много и жалко потоки. WebSocket — когда нужно живое общение туда-сюда без переподключения или вопрос стоит бесконечного потока временных рядов. Kafka — когда важна надёжность и асинхронность, и ты готов простить ей чуть бóльшую задержку ради гарантий и возможности переиграть историю.

Оставайся со мной на связи, я исследую мир разработки и структурную данные в своих обучающих проектах. Хочешь двигаться со мной жду тебя в тг — @karim_product

Если было полезно, поддержи подпиской. Мне будет мотивация продолжать в том же духе. Мой канал — https://t.me/+uH8Hm6kPWhU2OTc6

ссылка на оригинал статьи https://habr.com/ru/articles/1022974/