Kafka Client для Java: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в Apache Kafka Client для Java

Apache Kafka — это распределенная платформа потоковой передачи событий, которая позволяет публиковать, хранить и обрабатывать потоки данных в реальном времени. Библиотека Kafka Client для Java является официальным клиентом, предоставляющим API для взаимодействия с брокерами Kafka. Она позволяет Java-приложениям выступать в роли продюсеров (отправлять сообщения) и консьюмеров (читать сообщения), а также управлять метаданными и административными задачами.

Эта библиотека незаменима при построении микросервисных архитектур, систем обработки логов, ETL-пайплайнов, систем аналитики в реальном времени и любых других сценариев, где требуется надежная, масштабируемая и отказоустойчивая передача данных между компонентами.

Установка Kafka Client для Java

Kafka Client для Java распространяется через Maven Central Repository. Для добавления библиотеки в проект необходимо указать зависимость в файле pom.xml (для Maven) или build.gradle (для Gradle).

Maven

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>3.6.0</version> <!-- Используйте актуальную версию -->
</dependency>

Gradle

implementation 'org.apache.kafka:kafka-clients:3.6.0'

После добавления зависимости и обновления проекта все классы библиотеки будут доступны для импорта.

Быстрый старт: Producer и Consumer

Рассмотрим минимальный пример отправки и получения сообщения.

Producer (Отправитель)

import org.apache.kafka.clients.producer.*;
import java.util.Properties;

public class SimpleProducer {
    public static void main(String[] args) {
        // 1. Настройка свойств
        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");

        // 2. Создание продюсера
        Producer<String, String> producer = new KafkaProducer<>(props);

        // 3. Отправка сообщения
        ProducerRecord<String, String> record = new ProducerRecord<>("my-topic", "key1", "Hello, Kafka!");
        producer.send(record);

        // 4. Закрытие продюсера
        producer.close();
    }
}

Consumer (Получатель)

import org.apache.kafka.clients.consumer.*;
import java.time.Duration;
import java.util.Collections;
import java.util.Properties;

public class SimpleConsumer {
    public static void main(String[] args) {
        // 1. Настройка свойств
        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");
        props.put("auto.offset.reset", "earliest");

        // 2. Создание консьюмера
        Consumer<String, String> consumer = new KafkaConsumer<>(props);

        // 3. Подписка на топик
        consumer.subscribe(Collections.singletonList("my-topic"));

        // 4. Чтение сообщений
        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());
            }
        }
    }
}

Основные методы и функции библиотеки

Рассмотрим ключевые методы, которые используются при работе с Kafka Client.

1. KafkaProducer.send()

Что делает: Отправляет запись (ProducerRecord) в кластер Kafka. Это асинхронный метод, который немедленно возвращает Future.

Параметры:

  • ProducerRecord<K, V> record — запись, содержащая топик, ключ и значение.
  • Callback callback (опционально) — колбэк, вызываемый после подтверждения отправки.

Пример:

producer.send(new ProducerRecord<>("orders", "order-123", "{\"item\": \"laptop\"}"), 
    (metadata, exception) -> {
        if (exception != null) {
            System.err.println("Ошибка отправки: " + exception.getMessage());
        } else {
            System.out.println("Отправлено в partition " + metadata.partition() + " с offset " + metadata.offset());
        }
    });

2. KafkaProducer.flush()

Что делает: Принудительно отправляет все буферизированные записи в брокер. Блокирует выполнение до завершения отправки.

Параметры: Нет.

Пример:

producer.send(new ProducerRecord<>("logs", "error", "Critical failure"));
producer.flush(); // Гарантирует, что сообщение отправлено перед закрытием

3. KafkaConsumer.subscribe()

Что делает: Подписывает консьюмера на один или несколько топиков. Консьюмер будет получать сообщения из всех партиций этих топиков.

Параметры:

  • Collection<String> topics — список топиков для подписки.
  • ConsumerRebalanceListener listener (опционально) — слушатель событий перебалансировки.

Пример:

consumer.subscribe(Arrays.asList("topic1", "topic2"), new ConsumerRebalanceListener() {
    @Override
    public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
        System.out.println("Потеряны партиции: " + partitions);
    }

    @Override
    public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
        System.out.println("Назначены партиции: " + partitions);
    }
});

4. KafkaConsumer.poll()

Что делает: Извлекает записи из подписанных топиков. Блокирует выполнение на указанное время, если данных нет.

Параметры:

  • Duration timeout — максимальное время ожидания новых записей.

Пример:

ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
for (ConsumerRecord<String, String> record : records) {
    // Обработка записи
}

5. ConsumerRecords.iterator()

Что делает: Возвращает итератор по всем записям, полученным в результате вызова poll(). Позволяет последовательно обойти все записи.

Параметры: Нет.

Пример:

ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(500));
Iterator<ConsumerRecord<String, String>> iter = records.iterator();
while (iter.hasNext()) {
    ConsumerRecord<String, String> record = iter.next();
    System.out.println(record.value());
}

6. KafkaConsumer.commitSync()

Что делает: Синхронно фиксирует текущие смещения (offsets) для всех назначенных партиций. Блокирует выполнение до получения подтверждения от брокера.

Параметры:

  • Map<TopicPartition, OffsetAndMetadata> offsets (опционально) — конкретные смещения для фиксации.

Пример:

try {
    consumer.commitSync();
} catch (CommitFailedException e) {
    System.err.println("Не удалось зафиксировать offset: " + e.getMessage());
}

7. KafkaConsumer.commitAsync()

Что делает: Асинхронно фиксирует смещения. Не блокирует выполнение, можно указать колбэк для обработки результата.

Параметры:

  • OffsetCommitCallback callback (опционально) — колбэк, вызываемый после завершения фиксации.

Пример:

consumer.commitAsync((offsets, exception) -> {
    if (exception != null) {
        System.err.println("Ошибка асинхронного коммита: " + exception.getMessage());
    }
});

8. KafkaConsumer.seek()

Что делает: Перемещает указатель чтения консьюмера на конкретное смещение в указанной партиции. Позволяет повторно читать сообщения.

Параметры:

  • TopicPartition partition — партиция.
  • long offset — новое смещение.

Пример:

TopicPartition partition = new TopicPartition("my-topic", 0);
consumer.assign(Arrays.asList(partition));
consumer.seek(partition, 10); // Начать чтение с offset 10

9. KafkaConsumer.wakeup()

Что делает: Прерывает текущий или следующий вызов poll(). Используется для безопасного завершения работы консьюмера из другого потока.

Параметры: Нет.

Пример:

// В главном потоке
consumer.wakeup();

// В потоке консьюмера
try {
    while (true) {
        ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
        // обработка
    }
} catch (WakeupException e) {
    // Игнорируем, завершаем цикл
} finally {
    consumer.close();
}

10. KafkaConsumer.partitionsFor()

Что делает: Получает метаданные о партициях указанного топика: количество партиций, лидеры, реплики.

Параметры:

  • String topic — имя топика.

Пример:

List<PartitionInfo> partitions = consumer.partitionsFor("my-topic");
for (PartitionInfo p : partitions) {
    System.out.println("Partition: " + p.partition() + ", Leader: " + p.leader());
}

Реальный практический пример: Система обработки заказов

Создадим систему, которая принимает заказы от пользователей, отправляет их в Kafka, а затем обрабатывает (например, сохраняет в базу данных).

Producer: Отправка заказов

import org.apache.kafka.clients.producer.*;
import java.util.Properties;

public class OrderProducer {
    private final Producer<String, String> producer;

    public OrderProducer(String bootstrapServers) {
        Properties props = new Properties();
        props.put("bootstrap.servers", bootstrapServers);
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("acks", "all"); // Ожидать подтверждения от всех реплик
        this.producer = new KafkaProducer<>(props);
    }

    public void sendOrder(String orderId, String orderData) {
        ProducerRecord<String, String> record = new ProducerRecord<>("orders", orderId, orderData);
        producer.send(record, (metadata, exception) -> {
            if (exception == null) {
                System.out.println("Заказ " + orderId + " отправлен в партицию " + metadata.partition());
            } else {
                System.err.println("Ошибка отправки заказа " + orderId + ": " + exception.getMessage());
            }
        });
    }

    public void close() {
        producer.close();
    }

    public static void main(String[] args) {
        OrderProducer producer = new OrderProducer("localhost:9092");
        producer.sendOrder("order-001", "                    

Рекомендаций