カフカシリーズ第3弾!SpringBootプログラムのメッセージキューとしてKafkaを10分で使用する方法を学びますか?

関連資料:

 

  1. 入門!土語でカフカを知ることができます!

  2. 5分でカフカを体験してください

 

ステップ1:プロジェクトを作成する

Springが提供するSpringInitializrで直接作成することも、IDEAで直接作成することもできます。

ステップ2:Kafkaを構成する

application.yml構成ファイルを使用してKafkaの基本情報を構成します

server:
  port: 9090

spring:
  kafka:
    consumer:
      bootstrap-servers: localhost:9092
      # 配置消费者消息offset是否自动重置(消费者重连会能够接收最开始的消息)
      auto-offset-reset: earliest
    producer:
      bootstrap-servers: localhost:9092
      # 发送的对象信息变为json格式
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
kafka:
  topic:
    my-topic: my-topic
    my-topic2: my-topic2

Kafkaの追加の構成クラス:

package cn.javaguide.springbootkafka01sendobjects.config;

import org.apache.kafka.clients.admin.NewTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.support.converter.RecordMessageConverter;
import org.springframework.kafka.support.converter.StringJsonMessageConverter;

@Configuration
public class KafkaConfig {

    @Value("${kafka.topic.my-topic}")
    String myTopic;
    @Value("${kafka.topic.my-topic2}")
    String myTopic2;

    /**
     * JSON消息转换器
     */
    @Bean
    public RecordMessageConverter jsonConverter() {
        return new StringJsonMessageConverter();
    }

    /**
     * 通过注入一个 NewTopic 类型的 Bean 来创建 topic,如果 topic 已存在,则会忽略。
     */
    @Bean
    public NewTopic myTopic() {
        return new NewTopic(myTopic, 2, (short) 1);
    }

    @Bean
    public NewTopic myTopic2() {
        return new NewTopic(myTopic2, 1, (short) 1);
    }
}

このステップに到達したら、プロジェクトの実行を試みることができます。正常に実行された後、SpringBootが2つのトピックを作成することがわかります。

  1. my-topic:パーティションの数は2、レプリカの数は1
  2. my-topic2:パーティションの数は1、レプリカの数は1
「「

前のセクションで述べたように:kafka-topics --describe --zookeeper zoo1:2181 コマンドビューまたはKafkaビジュアル管理プラグインを介して直接-IDEAが提供するKafkalytic

ステップ3:送信するメッセージエンティティクラスを作成します

package cn.javaguide.springbootkafka01sendobjects.entity;

public class Book {
    private Long id;
    private String name;

    public Book() {
    }

    public Book(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    省略 getter/setter以及 toString方法
}

ステップ4:メッセージを送信するプロデューサーを作成する

「「

このステップは比較的長く、プロデューサーのコードを段階的に最適化します。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

@Service
public class BookProducerService {

    private static final Logger logger = LoggerFactory.getLogger(BookProducerService.class);

    private final KafkaTemplate<String, Object> kafkaTemplate;

    public BookProducerService(KafkaTemplate<String, Object> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    public void sendMessage(String topic, Object o) {
        kafkaTemplate.send(topic, o);
    }
}

Kafkaが提供するKafkaTemplate  呼び出し send()メソッドを使用して、  送信するトピックとメッセージコンテンツにアクセスすることで、メッセージ送信を簡単に完了できます。

  kafkaTemplate.send(topic, o);

送信されたメッセージの結果を知りたい場合、sendMessageメソッドは次のように記述されます。

    public void sendMessage(String topic, Object o) {
        try {
            SendResult<String, Object> sendResult = kafkaTemplate.send(topic, o).get();
            if (sendResult.getRecordMetadata() != null) {
                logger.info("生产者成功发送消息到" + sendResult.getProducerRecord().topic() + "-> " + sendResult.getProducerRecord().value().toString());
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

ただし、この種の同期送信方法は推奨されておらずFuture、オブジェクトの特性を利用していません 

KafkaTemplate  呼び出し元の send()メソッドは実際にListenableFuture オブジェクトを返します。

send()メソッドのソースコードは次のとおりです。

	@Override
	public ListenableFuture<SendResult<K, V>> send(String topic, @Nullable V data) {
		ProducerRecord<K, V> producerRecord = new ProducerRecord<>(topic, data);
		return doSend(producerRecord);
	}

ListenableFuture 継承されたFuture インターフェースを提供するのはSpringです

ListenableFutureメソッドのソースコードは次のとおりです。

public interface ListenableFuture<T> extends Future<T> {
    void addCallback(ListenableFutureCallback<? super T> var1);

    void addCallback(SuccessCallback<? super T> var1, FailureCallback var2);

    default CompletableFuture<T> completable() {
        CompletableFuture<T> completable = new DelegatingCompletableFuture(this);
        this.addCallback(completable::complete, completable::completeExceptionally);
        return completable;
    }
}

sendMessageメソッドの最適化を続ける

    public void sendMessage(String topic, Object o) {

        ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, o);
        future.addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {

            @Override
            public void onSuccess(SendResult<String, Object> sendResult) {
                logger.info("生产者成功发送消息到" + topic + "-> " + sendResult.getProducerRecord().value().toString());
            }
            @Override
            public void onFailure(Throwable throwable) {
                logger.error("生产者发送消息:{} 失败,原因:{}", o.toString(), throwable.getMessage());
            }
        });
    }

ラムダ式を使用して最適化を続行します。

    public void sendMessage(String topic, Object o) {

        ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, o);
        future.addCallback(result -> logger.info("生产者成功发送消息到topic:{} partition:{}的消息", result.getRecordMetadata().topic(), result.getRecordMetadata().partition()),
                ex -> logger.error("生产者发送消失败,原因:{}", ex.getMessage()));
    }

もう一度簡単にsend(String topic, @Nullable V data) 方法を調べてみましょう 

このsend(String topic, @Nullable V data)メソッドを使用すると、実際ProducerRecord新しいオブジェクト送信されます。

	@Override
	public ListenableFuture<SendResult<K, V>> send(String topic, @Nullable V data) {
		ProducerRecord<K, V> producerRecord = new ProducerRecord<>(topic, data);
		return doSend(producerRecord);
	}

ProducerRecordクラスには複数の構築メソッドがあります。

   public ProducerRecord(String topic, V value) {
        this(topic, null, null, null, value, null);
    }
    public ProducerRecord(String topic, Integer partition, Long timestamp, K key, V
        ......
    }

送信時にタイムスタンプ(timestamp)、キー、およびその他の情報を持ち込みたい場合、sendMessage()メソッドは次のように記述できます。

    public void sendMessage(String topic, Object o) {
      // 分区编号最好为 null,交给 kafka 自己去分配
        ProducerRecord<String, Object> producerRecord = new ProducerRecord<>(topic, null, System.currentTimeMillis(), String.valueOf(o.hashCode()), o);
      
        ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(producerRecord);
        future.addCallback(result -> logger.info("生产者成功发送消息到topic:{} partition:{}的消息", result.getRecordMetadata().topic(), result.getRecordMetadata().partition()),
                ex -> logger.error("生产者发送消失败,原因:{}", ex.getMessage()));
    }

ステップ5:メッセージを消費するコンシューマーを作成する

@KafkaListener メッセージ監視するメソッドの注釈を使用することにより  、メッセージがある場合、それらはポーリングによって消費されます。

import cn.javaguide.springbootkafka01sendobjects.entity.Book;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

@Service
public class BookConsumerService {

    @Value("${kafka.topic.my-topic}")
    private String myTopic;
    @Value("${kafka.topic.my-topic2}")
    private String myTopic2;
    private final Logger logger = LoggerFactory.getLogger(BookProducerService.class);
    private final ObjectMapper objectMapper = new ObjectMapper();


    @KafkaListener(topics = {"${kafka.topic.my-topic}"}, groupId = "group1")
    public void consumeMessage(ConsumerRecord<String, String> bookConsumerRecord) {
        try {
            Book book = objectMapper.readValue(bookConsumerRecord.value(), Book.class);
            logger.info("消费者消费topic:{} partition:{}的消息 -> {}", bookConsumerRecord.topic(), bookConsumerRecord.partition(), book.toString());
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

    @KafkaListener(topics = {"${kafka.topic.my-topic2}"}, groupId = "group2")
    public void consumeMessage2(Book book) {
        logger.info("消费者消费{}的消息 -> {}", myTopic2, book.toString());
    }
}

ステップ6:RESTコントローラーを作成する

import cn.javaguide.springbootkafka01sendobjects.entity.Book;
import cn.javaguide.springbootkafka01sendobjects.service.BookProducerService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping(value = "/book")
public class BookController {
    @Value("${kafka.topic.my-topic}")
    String myTopic;
    @Value("${kafka.topic.my-topic2}")
    String myTopic2;
    private final BookProducerService producer;
    private AtomicLong atomicLong = new AtomicLong();

    BookController(BookProducerService producer) {
        this.producer = producer;
    }

    @PostMapping
    public void sendMessageToKafkaTopic(@RequestParam("name") String name) {
        this.producer.sendMessage(myTopic, new Book(atomicLong.addAndGet(1), name));
        this.producer.sendMessage(myTopic2, new Book(atomicLong.addAndGet(1), name));
    }
}

ステップ7:テスト

次のコマンドを入力します。

curl -X POST -F 'name=Java' http://localhost:9090/book

コンソールによって出力される効果は次のとおりです。

my-topicには2つのパーティションがあります。複数のメッセージを送信しようとすると、メッセージが各パーティションに均等に送信されることがわかります。

おすすめ

転載: blog.csdn.net/baidu_39322753/article/details/104553862