Как ответить
Apache Kafka — это распределённая платформа для потоковой обработки событий. По сути, это высокопроизводительный брокер сообщений, который хранит данные на диске и позволяет перечитывать их. В отличие от классических очередей (RabbitMQ, ActiveMQ), Kafka не удаляет сообщения после доставки, а хранит их в течение заданного времени (retention). Это делает её удобной не только для асинхронной коммуникации, но и для построения event sourcing, CDC (Change Data Capture) и лог-агрегации.
Основные концепции:
- Топик — категория сообщений. Каждый топик разбит на партиции. Партиции — это единица параллелизма: продюсеры пишут в разные партиции, консюмеры читают из них.
- Оффсет — уникальный номер сообщения внутри партиции. Консюмер запоминает, какой оффсет он обработал, и может начать с любого места.
- Продюсер — отправляет сообщения в топик. Обычно указывает ключ, по которому сообщение попадает в определённую партицию (например, хэш ключа).
- Консюмер — читает сообщения из топика. Несколько консюмеров могут объединяться в группу: каждая партиция топика читается только одним консюмером из группы. Это даёт горизонтальное масштабирование — если добавить ещё консюмеров, нагрузка перераспределится.
- Брокер — сервер, который хранит данные. Кластер обычно состоит из нескольких брокеров. Каждая партиция реплицируется на несколько брокеров (фактор репликации). При падении одного брокера реплика берёт на себя чтение и запись.
Пример простого продюсера на Java:
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");
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
producer.send(new ProducerRecord<>("orders", "order-123", "{\"item\": \"laptop\"}"));
producer.close();
Пример консюмера (Java):
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "payment-service");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("orders"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
}
}
Типичные сценарии использования:
- Логгирование микросервисов — все сервисы пишут логи в один топик, а система мониторинга (например, ELK) читает их.
- Event Sourcing — сохраняем все изменения состояния как события, потом восстанавливаем состояние переигрыванием событий.
- CDC — Debezium читает изменения из базы данных (PostgreSQL, MySQL) и пишет их в Kafka.
- Потоковая обработка — Kafka Streams или ksqlDB позволяют делать агрегации, join-ы и оконные операции прямо в Kafka.
На практике важно понимать гарантии: при правильной настройке Kafka даёт at-least-once по умолчанию. Для exactly-once нужно включать идемпотентность продюсера и транзакции. Также нужно следить за размером партиций — слишком много партиций (тысячи) могут снизить производительность из-за накладных расходов на управление.