В каких случаях стоит использовать Apache Kafka, а в каких нет?

Ответ

Apache Kafka — распределенная потоковая платформа. Её применение должно быть обосновано архитектурными требованиями.

✅ Когда Kafka — хороший выбор:

  • Обработка потоков событий в реальном времени: логи, телеметрия, клики.
  • Высокая пропускная способность и горизонтальное масштабирование.
  • Требуется гарантированная доставка и отказоустойчивость (репликация).
  • Архитектура на основе событий (Event-Driven Architecture): декoupling сервисов.
  • Необходимость повторной обработки данных (хранение сообщений длительное время).

❌ Когда Kafka — избыточна или не подходит:

  • Простые синхронные RPC-вызовы между двумя сервисами (используйте REST/gRPC).
  • Очереди задач (Job Queues) с одним потребителем на сообщение (рассмотрите RabbitMQ).
  • Сценарии с очень низкой нагрузкой, где сложность администрирования Kafka не окупается.
  • Требуется строгий глобальный порядок сообщений (Kafka гарантирует порядок только в пределах партиции).
  • Транзакции, затрагивающие несколько источников данных (Sagas поверх Kafka сложны).

Пример Producer/Consumer:

// Producer
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

try (Producer<String, String> producer = new KafkaProducer<>(props)) {
    producer.send(new ProducerRecord<>("user-events", "user123", "{"action":"login"}"));
}

// Consumer
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "my-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)) {
    consumer.subscribe(List.of("user-events"));
    while (true) {
        ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
        for (ConsumerRecord<String, String> record : records) {
            System.out.println("Processed: " + record.value());
        }
    }
}

Выбор инструмента должен соответствовать задаче.

Ответ 18+ 🔞

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

✅ Вот когда Кафка — это прям то, что доктор прописал, ёпта:

  • Потоки событий в реальном времени: логи, телеметрия, клики — всё это пиздец как летит, а Кафка всё проглотит и не поперхнётся.
  • Нагрузка овердохуища и надо масштабироваться: Добавляй брокеров, плоди партиции — она только рада будет.
  • Надо, чтобы ни одно сообщение не потерялось: Репликация, гарантии доставки — она за этим следит, как хитрая жопа.
  • Архитектура на событиях (EDA): Хочешь, чтобы сервисы не знали друг о друге в лицо, а общались через события? Вот тут её родная стихия.
  • Хочешь иметь возможность отмотать время и перечитать данные: Сообщения хранятся — бери и читай с любого места, хоть с позавчера.

❌ А вот когда Кафка — это пиздопроебина и стрельба из пушки по воробьям:

  • Простые синхронные вызовы типа "скажи-мне-ответ": Ты чё, обкурился? Для этого есть REST или gRPC. Не надо тут Кафку ебать.
  • Очередь задач, где одно сообщение — один работяга: Для этого RabbitMQ или что-то попроще. В Кафке consumer group — это не совсем то.
  • Нагрузка — три сообщения в час: Администрировать зоопарк Кафки ради этого — это, блядь, волнение ебать. Не окупится.
  • Нужен строгий глобальный порядок на все сообщения: Забудь. В Кафке порядок гарантирован только внутри одной партиции. Если это критично — готовься к боли.
  • Сложные распределённые транзакции через несколько баз: Саги поверх Кафки — это тот ещё геморрой. Лучше даже не начинать.

Смотри, как примерно выглядит код, чтоб понимал масштаб:

// Тот, кто отправляет (Producer)
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

try (Producer<String, String> producer = new KafkaProducer<>(props)) {
    producer.send(new ProducerRecord<>("user-events", "user123", "{"action":"login"}"));
}

// Тот, кто читает (Consumer)
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "my-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)) {
    consumer.subscribe(List.of("user-events"));
    while (true) {
        ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
        for (ConsumerRecord<String, String> record : records) {
            System.out.println("Processed: " + record.value());
        }
    }
}

Короче, суть в чём: бери инструмент под задачу, а не тащи Кафку везде, куда глаза глядят. Иначе накроешься медным тазом со своей "современной архитектурой".