Springboot подключается к кластеру Kafka (версия Kafka 2.13-3.4.0)

1. Строительство окружающей среды

1.1 среда Springboot

JDK 11+
Maven 3.8.x+
Springboot 2.5.4 +

1.2 зависимости Кафки

Импорт pom-файла Springboot

       <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>3.4.0</version>
        </dependency>

2. Класс конфигурации Kafka

2.1 Издатель

2.1.1 Конфигурация

Издатель Мы используем KafkaTemplate для публикации сообщений, поэтому сначала нам необходимо его настроить.

@Configuration
@EnableKafka
public class KafkaConfig {
    
    


     /***** 发布者 *****/

    //生产者工厂
    @Bean
    public ProducerFactory<Integer, String> producerFactory() {
    
    
        return new DefaultKafkaProducerFactory<>(producerConfigs());
    }

    //生产者配置
    @Bean
    public Map<String, Object> producerConfigs() {
    
    
        Map<String, Object> props = new HashMap<>();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.2.83:9092,192.168.2.84:9092,192.168.2.86:9092");
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, IntegerSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        return props;
    }

    //生产者模板
    @Bean
    public KafkaTemplate<Integer, String> kafkaTemplate() {
    
    
        return new KafkaTemplate<>(producerFactory());
    }
}

2.1.2 Создание класса издателя

После настройки издателя следующим шагом будет публикация сообщения. Нам необходимо наследовать интерфейс ProducerListener<K, V>. Полная информация об интерфейсе выглядит следующим образом:

public interface ProducerListener<K, V> {
    
    

    void onSuccess(ProducerRecord<K, V> producerRecord, RecordMetadata recordMetadata);

    void onError(ProducerRecord<K, V> producerRecord, RecordMetadata recordMetadata,
            Exception exception);

}

Реализуя метод этого интерфейса, мы можем получить асинхронный обратный вызов, содержащий результат отправки (успех или неудача), то есть результат отправки можно получить в реализации этого интерфейса.

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

@Component
public class KafkaProducer implements ProducerListener<Object,Object> {
    
    

    private static final Logger producerlog = LoggerFactory.getLogger(KafkaProducer.class);

    private final KafkaTemplate<Integer, String> kafkaTemplate;

    public KafkaProducer(KafkaTemplate<Integer, String> kafkaTemplate) {
    
    
        this.kafkaTemplate = kafkaTemplate;
    }

    public void producer (String msg,String topic){
    
    
        ListenableFuture<SendResult<Integer, String>> future = kafkaTemplate.send(topic,0, msg);
        future.addCallback(new KafkaSendCallback<Integer, String>() {
    
    

            @Override
            public void onSuccess(SendResult<Integer, String> result) {
    
    
                producerlog.info("发送成功 {}", result);
            }

            @Override
            public void onFailure(KafkaProducerException ex) {
    
    
                ProducerRecord<Integer, String> failed = ex.getFailedProducerRecord();
                producerlog.info("发送失败 {}",failed);
            }

        });
    }

}

2.1.3 Публикация сообщения

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

@RestController
public class KafkaTestController {
    
    

    private static final Logger kafkaTestLog = LoggerFactory.getLogger(KafkaTestController.class);

    @Resource
    private KafkaProducer kafkaProducer;

    @GetMapping("/kafkaTest")
    public void kafkaTest(String msg,String topic){
    
    
        kafkaProducer.producer(msg,topic);
        kafkaTestLog.info("接收到消息 {} {}",msg,topic);
    }
}

Все готово, запускаем программу и с помощью почтальона проводим простой тест.

Чтобы опубликовать сообщение:
вставьте сюда описание изображения

Результат публикации:
вставьте сюда описание изображения
вы можете видеть, что сообщение отправлено успешно.

Давайте посмотрим, получил ли потребитель Kafka сообщение:

вставьте сюда описание изображения

Вы можете видеть, что потребители Kakfa также получают это сообщение.

2.2 Потребители

2.2.1 Конфигурация

Существует множество способов получения сообщений. Здесь мы решили использовать аннотацию @KafkaListener для приема сообщений. Он используется следующим образом:

public class Listener {
    
    

    @KafkaListener(id = "foo", topics = "myTopic", clientIdPrefix = "myClientId")
    public void listen(String data) {
    
    
        ...
    }

}

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

Мы продолжаем настраивать потребителей в исходном классе конфигурации Kafka KafkaConfig, вероятно, следующим образом.

@Configuration
@EnableKafka
public class KafkaConfig {
    
    


     /***** 发布者 *****/

    //生产者工厂
    @Bean
    public ProducerFactory<Integer, String> producerFactory() {
    
    
        return new DefaultKafkaProducerFactory<>(producerConfigs());
    }

    //生产者配置
    @Bean
    public Map<String, Object> producerConfigs() {
    
    
        Map<String, Object> props = new HashMap<>();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.2.83:9092,192.168.2.84:9092,192.168.2.86:9092");
           props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, IntegerSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        return props;
    }

    //生产者模板
    @Bean
    public KafkaTemplate<Integer, String> kafkaTemplate() {
    
    
        return new KafkaTemplate<>(producerFactory());
    }

    /***** 消费者 *****/

    //容器监听工厂
    @Bean
    KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<Integer, String>>
    kafkaListenerContainerFactory() {
    
    
        ConcurrentKafkaListenerContainerFactory<Integer, String> factory =
                new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        factory.setConcurrency(3);
        factory.getContainerProperties().setPollTimeout(3000);
        return factory;
    }

    //消费者工厂
    @Bean
    public ConsumerFactory<Integer, String> consumerFactory() {
    
    
        return new DefaultKafkaConsumerFactory<>(consumerConfigs());
    }

    //消费者配置
    @Bean
    public Map<String, Object> consumerConfigs() {
    
    
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.2.83:9092,192.168.2.84:9092,192.168.2.86:9092");
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, IntegerDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS, JsonDeserializer.class);
        props.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, JsonDeserializer.class.getName());
        props.put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG,3000);
        return props;
    }
}

Обратите внимание: для установки свойств контейнера необходимо использовать фабричный метод getContainerProperties(). Он используется в качестве шаблона для фактических свойств, внедряемых в контейнер.

2.2.2 Создание потребительского класса

После настройки мы можем использовать эту аннотацию. Эту аннотацию можно использовать по-разному:

1. Используйте его, чтобы переопределить параллелизм и свойства фабрики контейнеров.

@KafkaListener(id = "myListener", topics = "myTopic",
        autoStartup = "${listen.auto.start:true}", concurrency = "${listen.concurrency:3}")
public void listen(String data) {
    
    
    ...
}

2. Вы можете использовать явные темы и разделы (и необязательные начальные смещения).

@KafkaListener(id = "thing2", topicPartitions =
        {
    
     @TopicPartition(topic = "topic1", partitions = {
    
     "0", "1" }),
          @TopicPartition(topic = "topic2", partitions = "0",
             partitionOffsets = @PartitionOffset(partition = "1", initialOffset = "100"))
        })
public void listen(ConsumerRecord<?, ?> record) {
    
    
    ...
}


3. Примените начальное смещение ко всем выделенным разделам.

@KafkaListener(id = "thing3", topicPartitions =
        {
    
     @TopicPartition(topic = "topic1", partitions = {
    
     "0", "1" },
             partitionOffsets = @PartitionOffset(partition = "*", initialOffset = "0"))
        })
public void listen(ConsumerRecord<?, ?> record) {
    
    
    ...
}


4. Укажите список разделов через запятую или диапазон разделов.

@KafkaListener(id = "pp", autoStartup = "false",
        topicPartitions = @TopicPartition(topic = "topic1",
                partitions = "0-5, 7, 10-15"))
public void process(String in) {
    
    
    ...
}


5. Слушателю может быть предоставлено подтверждение.

@KafkaListener(id = "cat", topics = "myTopic",
          containerFactory = "kafkaManualAckListenerContainerFactory")
public void listen(String data, Acknowledgment ack) {
    
    
    ...
    ack.acknowledge();
}


6. Добавьте заголовки

@KafkaListener(id = "list", topics = "myTopic", containerFactory = "batchFactory")
public void listen(List<String> list,
        @Header(KafkaHeaders.RECEIVED_KEY) List<Integer> keys,
        @Header(KafkaHeaders.RECEIVED_PARTITION) List<Integer> partitions,
        @Header(KafkaHeaders.RECEIVED_TOPIC) List<String> topics,
        @Header(KafkaHeaders.OFFSET) List<Long> offsets) {
    
    
    ...
}


Давайте напишем здесь простой, просто используйте его для принятия данных указанной темы:

@Component
public class KafkaConsumer {
    
    

    private static final Logger consumerlog = LoggerFactory.getLogger(KafkaConsumer.class);

    @KafkaListener(topicPartitions  = @TopicPartition(topic = "kafka-topic-test",
            partitions = "0"))
    public void consumer (String data){
    
    
        consumerlog.info("消费者接收数据 {}",data);
    }
}

Позвольте мне объяснить здесь, поскольку мы вручную назначили темы/разделы, group.id в аннотации может быть пустым. Чтобы указать group.id, добавьте props.put(ConsumerConfig.GROUP_ID_CONFIG, «bzt001»); или добавьте groupId = «идентификатор группы» после аннотации @TopicPartition в конфигурации потребителя.

2.2.3 Потребление сообщений

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

Supongo que te gusta

Origin blog.csdn.net/qq_35241329/article/details/131119920
Recomendado
Clasificación