前言

生产者

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.Properties;

public class Producer {
    public static void main(String[] args) {
        Properties props = new Properties();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.0.1:29092");

        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

        try (KafkaProducer<String, String> producer = new KafkaProducer<>(props)) {
            String topic = "my-test-topic";

            ProducerRecord<String, String> record = new ProducerRecord<>(topic, "key", "value");
            // 异步发送消息
            producer.send(record, (metadata, exception) -> {
                if (exception == null) {
                    System.out.printf("Topic: %s, Partition: %d, Offset: %d%n",
                            metadata.topic(), metadata.partition(), metadata.offset());
                } else {
                    exception.printStackTrace();
                }
            });
            producer.flush();
        }
    }
}

消费者

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.time.Duration;
import java.util.Collections;
import java.util.Properties;


public class Consumer {
    public static void main(String[] args) {
        Properties props = new Properties();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.0.1:29092");
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "test-consumer-group");

        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

        try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)) {
            consumer.subscribe(Collections.singletonList("my-test-topic"));
            while (true) {
                var messages = consumer.poll(Duration.ofMillis(100));
                if (messages != null) {
                    for (var message : messages) {
                        System.out.println(message);
                    }
                }
            }
        }
    }
}

消费者组

当需要将同一份完整的数据,同时交给完全不同的业务系统去独立处理时,就需要创建使用到消费者组

场景举例:假设用户下了一笔订单,订单服务向 order-created-topic 发送了一条消息。下游有三个业务系统需要消费这条消息:

  • 库存服务(Group A): 需要消费这条消息去扣减商品库存。
  • 积分服务(Group B): 需要消费这条消息给用户增加会员积分。
  • 通知服务(Group C): 需要消费这条消息给用户发送下单成功的短信。

为什么用多组:这三个服务是完全独立的业务逻辑。使用不同的组,Kafka 会把每一条订单消息完整地复制(广播)给这三个组,每个消费者组都有独立的 Offset,互不干扰。

主题

Offset

Rebalance

幂等